Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Adding implicits to expression's scope #18705

Unanswered
kciesielski asked this question in Metaprogramming
Discussion options

I'm working on a problem (Scala 3 only), where my generated code has to call library code for typeclass derivation, but this derivation needs some implicits in scope. My inline method on top of this process first dynamically constructs these dependencies, and then I'd like to turn them into givens, to achieve something like:

given Pickler[Field1Type] = ...
given Pickler[Field2Type] = ...
// more givens, depending on the previous step of the process
External.derive[T] // macro call

In my macro, the givens are generated as a list of expressions. Then I create the result expression with Expr.block, but my derive call can't see the givens. I also tried to construct a tree using Symbols, Apply, etc., and convert it to Expr afterwards, but the final effect is the same. The result expression can be printed as a code and it looks like the example shown above, which works as expected when copied and pasted in isolation.

The problem can be showcased in this minimal example:

 inline def deriveCodec: Int = ${ macros.deriveCodecMacro }
 def deriveCodecMacro(using Quotes): Expr[Int] =
 Expr.summon[Int].get
 def testMacro(using Quotes): Expr[Unit] =
 Expr.block(
 List('{ // this list of givens was generated dynamically
 implicit val i: Int = 123 
 }),
 '{
 println(deriveCodec)
 }
 )

Generated code printed with TreeAnsiCode is:

{
 implicit val i: scala.Int = 123
 scala.Predef.println(sttp.tapir.json.pickler.macros.deriveCodec)
}

and works as expected when just copied and pasted (prints 123). However, it doesn't work when testMacro is called - summon returns None.
(BTW I used implicit instead of given to make sure generated code indeed creates an implicit. For some reason using given and printing generated code gives final lazy val given_Int: scala.Int = 123 without the given keyword.)

I suspect that there's some limitation which doesn't allow the implicit scopes of expressions to be dynamically generated or altered in a macro, like I'm trying to. Could someone confirm this?

You must be logged in to vote

Replies: 1 comment 3 replies

Comment options

Maybe something like this could help

import scala.quoted.*
def givenExpression[T: Type, U: Type](e: Expr[T])(f: Expr[T ?=> U])(using Quotes): Expr[U] =
 '{ implicit val x: T = $e; $f(using x) }
def fun(using a: Int): Int = a
def test(using Quotes) =
 givenExpression('{23})('{ fun }) // generates: { implicit val x = 123; fun(using x) }
You must be logged in to vote
3 replies
Comment options

Thanks! Sounds promising, although my problem is for an arbitrary number of implicits. That would require to generate def fun(...) as well, since we don't know the list of using params up front. Would that be possible?

Comment options

It does not sound easy/possible if you want to have it statically checked.

Comment options

Is there a way to do this in a non-statically checked way?

The deriveCodecMacro from the example is fixed, and we need to call it so that it uses "our" dynamically-generated givens/implicits. The third-party macro looks up implicits using Expr.summon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

AltStyle によって変換されたページ (->オリジナル) /