Proposal: Add Expr.summonIgnoring method to use in macros #21909
Replies: 5 comments 12 replies
-
Prototype implementation: jchyb@d97ec4e |
Beta Was this translation helpful? Give feedback.
-
fyi @arainko (not sure if relevant, but just in case...) |
Beta Was this translation helpful? Give feedback.
-
We were talking about, @ahoy-jon, so this might interest you as well :) I'd love to have this and something like this in 2.13 |
Beta Was this translation helpful? Give feedback.
-
Speaking of the workaround, wouldn't this keep working with the new implicit search rules if we get rid of the inheritance relation between trait Transformer[From, To]:
def transform(from: From): To
object Transformer:
opaque type AutoDerived[From, To] = Transformer[From, To]
object AutoDerived:
inline given auto[From, To]: Transformer.AutoDerived[From, To] =
// in this method we can search for Transformer[_, _], effectively disallowing recursive macro expansions
new Transformer[From, To]:
def transform(from: From): To = ???
inline given fromAuto[From, To](using a: AutoDerived[From, To]): Transformer[From, To] = a
def define[From, To](fun: From => To): Transformer[From, To] =
new Transformer[From, To]:
def transform(from: From) = fun(from)
extension [From](from: From) def transformInto[To](using t: Transformer[From, To]): To = t.transform(from) case class Foo(a: Int, b: String)
object Foo {
given totalTransformer: Transformer[Foo, Bar] = Transformer.define[Foo, Bar](foo => Bar(foo.a, foo.b))
}
case class Bar(a: Int, b: String)
case class Baz(a: Int, b: String)
@main def main() = {
println(summon[Transformer.AutoDerived[Foo, Bar]])
println(summon[Transformer[Foo, Bar]])
println(summon[Transformer.AutoDerived[Foo, Baz]])
println(summon[Transformer[Foo, Baz]])
} |
Beta Was this translation helpful? Give feedback.
-
Does this affect regular code using |
Beta Was this translation helpful? Give feedback.
-
Motivation:
Typeclass derivation libraries have long been using the implicit resolution feature in scala. It:
Auto derivation can be also implemented using macros, with the main benefit being added performance (at the cost of more difficult library developer experience compared to using the usual shapeless/mirror use), and more control over the generated typeclasses. An issue arises when we want to mix that benefit of gained performance with the ability for users do define those additional typeclasses. Since it is currently impossible to disallow recursive summons we cannot first check on the existence of a user defined given typeclass and then create it, if necessary, as part of the same macro call, we have to structure like we would mirror-based or shapeless-based macro derivation, where we end up with macro methods expanding macro methods as part of their derivation.
However that approach can worsen performance:
Workarounds
There historically existed few workarounds for this, some of which we can no longer use:
Scala 2 could achieve something like this with its
inferImplicitValue
(https://www.scala-lang.org/api/2.13.2/scala-reflect/scala/reflect/macros/Typers.html#openMacros:List[scala.reflect.macros.blackbox.Context]) method it had as part of its API. That method, used similarly to Expr.summon in macros, had an argumentwithMacrosDisabled: Boolean
which would naturally guard against recursive macro calls, although disallowing other possible functionality with it.Another workaround for this was via assigning a different type to a TypeClass obtained from macro (found and used in the chimney library). However, this stopped working with the recent changes to given prioritization, where the following issue can occur:
Here we have a Transformer (user defined typeclass) and Transformer.AutoDerived (returned by a macro). This way we are able to summon user defined typeclass in the macro method without rerunning the macro method again. With given prioritization changes introduced in 3.7.0 the compiler will starts seeing those 2 as ambiguous. It seems like this can be worked around again by adding a call to summonFrom in inlined transformInto, continuing the game of cat and mouse until another change/bugfix to implicit resolution.
Proposal
The proposal here is about having an officially sanctioned way of achieving the same result as the above workarounds:
Where in addition to the searched type, we can pass symbols which will not be used as part of the implicit search, with the main use case being disallowing recursive macro expansion.
Beta Was this translation helpful? Give feedback.
All reactions