I will try it out and have a complete example. The trick will be to use a pattern arg match { case '{ $arg: $t } => to recover the type of the argument. Iāll have a look at the details.
Weāre currently without them, and have been told to do what we need to with current metaprogramming capabilities (which means in this case, mostly macros), or implement curried varargs using the same.
My primary usecase for curried varargs is exactly what youāve proposed (Iāve included in a previous comment an extremely similar sample implementation using curried varargs). Itās also considerably simpler than what weād need to do to implement curried varargs with a macro, so it provides a useful canary.
Thatās an interesting bit of syntax, I had to go back through the docs to see if Iād missed that. I could only find examples where that was part of the match, not a value bound by the match. If it works, that would be awesome.
Something needs to be done about the lack of documentation for this, so much is missing that right now macros seem very much like something reserved for the High Priesthood.
Looks like underlyingArgument turned out to be a red herring. I was playing around with what you suggested, and it ended up very close structurally to what youāve got but throws this error during compilation:
[error] 10 | assertEquals(show"${1}", "")
[error] | ^^^^^^^^^^
[error] |Exception occurred while executing macro expansion.
[error] |java.lang.ClassCastException: dotty.tools.dotc.ast.Trees$Literal cannot be cast to scala.quoted.Expr
[error] | at sql2json.cat.Show$.$anonfun$1(Show.scala:32)
[error] | at scala.collection.immutable.List.foreach(List.scala:305)
[error] | at sql2json.cat.Show$.showInterpolatorImpl(Show.scala:38)
[error] |
[error] | This location is in code that was inlined at ShowInstancesTest.scala:10
For posterity, this is my non-working version:
def showInterpolatorImpl(sc: Expr[StringContext], argsExpr: Expr[Seq[Any]])(given qctx: QuoteContext): Expr[String] =
import qctx.tasty.{_, given}
val shownArgs: List[Expr[String]] = argsExpr.unseal.underlyingArgument match
case Typed(Repeated(args, _), _) =>
args.foreach {
case '{ $arg: $t } =>
searchImplicitExpr[Show[$t]] match
case None =>
qctx.error(s"No Show instance found for $arg", argsExpr)
'{""}
case Some(showImpl) =>
'{$showImpl.show($arg)}
}
Nil
case _ =>
qctx.error("Expected statically known argument list", argsExpr)
Nil
'{ $sc.s(${Expr.ofList(shownArgs)}) }
inline def (sc: => StringContext) show (args: Any*): String = ${ showInterpolatorImpl('sc, 'args) }
Thatās basically the Scala 2 implementation, unfortunately (or fortunately, depending on your view of implicit conversions), unless my build was just doing something weird, you need to either import the feature flag in both the definition scope and callsite scope, or you need to enable the compiler flag for implicit conversions.
As I mentioned above, this makes no sense as an opt-in path for something that exists to make code easier to reason about.
12:57:14 $ bin/dotr
Starting dotty REPL...
scala> class Token(str: String)
// defined class Token
scala> given Conversion[String, Token] = new Token(_)
def given_Conversion_String_Token: Conversion[String, Token]
scala> def x: Token = "foo"
1 |def x: Token = "foo"
| ^^^^^
|Use of implicit conversion trait Function1 in package scala should be enabled
|by adding the import clause 'import scala.language.implicitConversions'
|or by setting the compiler option -language:implicitConversions.
|See the Scala docs for value scala.language.implicitConversions for a discussion
|why the feature should be explicitly enabled.
def x: Token
Notably, this proposed feature has landed in C# 13. Not quite as specced here (they only transform the varargs param, and not the entire method call) but pretty close and served a similar purpose