I’m aware of searchImplicitExpr and summonFrom, I’ve gone over what’s in the docs multiple times. Once I get the type, summoning the instance isn’t a problem.
Getting the type is the problem, so if your example could include a case where the type isn’t known ahead of time (specifically, going from Expr[Any] or Term to Type) it would be extremely helpful.
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