I am currently studying Scala Multi Stage Programming from this book
Scalable Metaprogramming in Scala 3
, taken from the author website https://github.com/nicolasstucki/nicolasstucki/raw/main/Scalable%20Metaprogramming%20in%20Scala%203.pdf
This question is about the last snippet, the one given at page 151 related to the Reflection API.
class memoize extends MacroAnnotation:
override def transform(using Quotes)(tree: quotes.reflect.Definition): List[quotes.reflect.Definition] =
import quotes.reflect._
tree match
case DefDef(name, params@List(TermParamClause(List(param))), tpt, Some(impl)) =>
(Ref(param.symbol).asExpr, impl.asExpr) match
case ('{ $paramRef: arg }, '{ $implExpr: res }) =>
val cacheSymbol = Symbol.newVal(tree.symbol.owner, name + "Cache"
,
TypeRepr.of[Map[arg, res]], Flags.EmptyFlags, Symbol.noSymbol)
val fibCacheRef = Ref(cacheSymbol).asExprOf[Map[arg, res]]
val cacheVal = ValDef(cacheSymbol, Some('{ Map.empty[arg, res] }.asTerm))
val newDef = DefDef.copy(tree)(name, params, tpt, Some(
'{
if !$fibCacheRef.contains($paramRef) then
$fibCacheRef($paramRef) = $implExpr
$fibCacheRef($paramRef)
}.asTerm
))
case _ => List(cacheVal, newDef)
report.errorAndAbort("expected a`def` with a single parameter list")
}
I can't get the compiler past the function argument that is declared with Definition type. I've also tried using the import scheme given at the first snippet at https://scala-lang.org/api/3.3_LTS/docs/docs/reference/metaprogramming/reflection.html
resulting in
import scala.quoted.*
object Main {
@_root_.scala.annotation.experimental
class memoize extends MacroAnnotation {
override def transform(using Quotes)(tree: reflect.Definition): List[reflect.Definition] = {
import quoted.quotes.reflect.*
...
}
}
}
but the compiler complains with
[error] 10 | override def transform(using Quotes)(tree: reflect.Definition): List[reflect.Definition] = {
[error] | ^^^^^^^^^^^^^^^^^^
[error] | type Definition is not a member of reflect
Any clue or advice?
I am currently pointing the finger at scala2.13
retrocompatibility which misses this functionality. Still haven't found my way out.
Have a great evening, I hope this message won't catch you in your busiest moment.
Regards
reflect
module is part of quotes: Quotes
value, which is usually imported inside a method:
// named Quotes instance
def macroMethod(arguments)(using quoted: Quotes): Result = {
import quoted.* // reflect et all
}
// anonymous Quotes instance
def macroMethod(arguments)(using Quotes): Result = {
// uses:
// transparent inline def quotes(using q: Quotes): q.type = q
import quotes.* // reflect et all
}
To make it available in the signature of a macro annotation, which uses reflect.Definition
, you have to use named Quoted
instance, and then a path-dependent type:
import scala.annotation.experimental
@experimental
class memoize extends MacroAnnotation {
override def transform(using q: Quotes)(
tree: q.reflect.Definition // <- q. prefix needed
): List[q.reflect.Definition] = { // <- q. prefix needed
import q.* // from here on, q. prefix is no lonfer needed
...
}
}