Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: embedded string interpolations #44

Open
danicheg opened this issue May 12, 2022 · 1 comment
Open

RFC: embedded string interpolations #44

danicheg opened this issue May 12, 2022 · 1 comment

Comments

@danicheg
Copy link
Member

danicheg commented May 12, 2022

This issue is a follow-up from http4s/http4s#3433.
I've struggled with this a bit and think it's quite problematic to embed string interpolations to uri"" and path"" of http4s (actually, it's so for every interpolated macros based on literally):

  1. At the moment, compilation fails if invalid strings have been passed (underlying validate is calling at compile time).
  2. Say
val foo = "/foo/bar"
val someuri = uri"https://example.com$foo"

means that we should check the value of foo at the runtime (generally, foo could be some computation, not a constant value). So bringing embedded string interpolations means we should proceed with runtime values at compile time (because of 1). I'm not that much a Scala macros astronaut, but am feeling it's probably impossible.

UPD: speaking more accurately, compile-time checking is only possible for the literal part of the passed string.

If anyone has insights about that, please share them here.

@mpilquist
Copy link
Member

Yes, that's correct. It's possible to support interpolation of constant values at compile time, but not possible to use non-constant values. Doing so would require changing the macro to return an Either[String, Uri] when there are one or more non-constant parts and Uri otherwise. This type switching would require a white box macro in Scala 2 and transparent inline in Scala 3.

To support interpolation of constant values, we could do something like this:

trait Literally[A]:
  type Quotes = scala.quoted.Quotes
  type Expr[A] = scala.quoted.Expr[A]
  val Expr = scala.quoted.Expr

  def validate(s: String)(using Quotes): Either[String, Expr[A]]

  def apply(strCtxExpr: Expr[StringContext], argsExpr: Expr[Seq[String]])(using Quotes): Expr[A] =
    apply(strCtxExpr.valueOrAbort.parts, argsExpr.valueOrAbort)

  private def apply(parts: Seq[String], args: Seq[String])(using Quotes): Expr[A] =
    val str = parts.zipAll(args, "", "").map(t => t(0) + t(1)).mkString("", "", "")
    validate(str) match
      case Left(err) =>
        quotes.reflect.report.error(err)
        ???
      case Right(a) =>
        a

Usage:

scala> inline val x = "4"

scala> val y = port"1${x}2"
val y: org.typelevel.literally.examples.Port = Port(142)

This is too limited though as it requires the args list to be strings. It would be nicer to support arbitrary types there. Overall, I don't think the complexity is worth it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants