Search code examples
scalareflectionmetaprogrammingmultistage

Scala 3 - Reflection API - type Definition is not a member of reflect


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


Solution

  • 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
        ...
      } 
    }