Nontrivial compile-time string interpolation pattern-matching in Scala 3

If we want to do compile-time pattern matching checks for string interpolation in Scala 2 we write:

implicit class StrInterp(sc : StringContext) {
  object si {
    def apply(args : Any*) : Any = macro siApplyMacro
    def unapply(arg : Any): Option[Seq[Any]] = macro siUnapplyMacro
  }
}

val x = arg match {
  case si"my_pattern" => ???
}

In Scala 3 this is nontrivial:

  • Extension methods are limited to, well, methods, so an object cannot be placed as an extension.
  • Implicit classes cannot receive an inline StringContext.

The way to do it is:

class SIParts[+P <: Tuple](parts : P): //P is a tuple representation of the SC parts
  transparent inline def apply(inline args: Any*): Any = ${siApplyMacro[P]('args, 'parts)}
  transparent inline def unapplySeq(inline arg: Any): Option[Seq[Any]] = ${siUnapplyMacro[P]('arg, `parts)}

extension (inline sc : StringContext)
  transparent inline def si : SIParts[Tuple] = ${siPartsMacro('sc)}

To me this seems highly burdensome and nontrivial.
Is there a better way to do this currently?
Is there a way to design the language better so this feature will be at least at par with Scala 2? E.g., allow extension fields (lazy vals and objects) and not just methods.

2 Likes

In case you haven’t seen it, I published https://github.com/typelevel/literally to help with this type of problem.

1 Like

Nice, but that still does not solve the unapply problem. The apply is trivial and there is no need to use an object for that.

Yep agreed, just wanted to mention it since it’s tangentially related. :slight_smile:

Update:
There is an open-issue on the dotty tracker.