When are Scala macro annotations executed? (macro paradise)

360 views Asked by At

I tried to implement the Scala macro annotations example as described in the documentation. I managed to compile the macro annotations before the actual project which uses them, i.e., the @compileTimeOnly("enable macro paradise to expand macro annotations") does not get triggered meaning that the macro annotation is compiled before its usage. So far so good.

However, when I annotate certain values in my actual project as follows:

@identity val foo: Double = 1.1
@identity val bar: String = "bar"

then I expect the following print to happen when running the main project (by the macro annotation example linked before):

(<empty>,List(val foo: Double = 1.1))
(<empty>,List(val bar: String = "bar"))

This is where I get confused, the print does not happen when I run the main project. It does however, appear for a split second when compiling the main project as a warning?

(I'm using the IntelliJ IDEA and Scala 2.12.8)

1

There are 1 answers

2
Alexey Romanov On BEST ANSWER

I managed to compile the macro annotations before the actual project which uses them, i.e., the @compileTimeOnly("enable macro paradise to expand macro annotations") does not get triggered meaning that the macro annotation is compiled before its usage

No, @compileTimeOnly being triggered would mean the annotation is present after code using it is compiled. So its not being triggered means the macro has been executed during the compilation. And since println is in the body of the macro, not in the transformed code, that's when you see the output.

If you want printing to happen when running the project, you need to modify the return value which contains the transformed code, i.e. the last two lines in the example:

val outputs = expandees
c.Expr[Any](Block(outputs, Literal(Constant(()))))

either using quasiquotes or directly manipulating ASTs.

Untested, but using quasiquotes something approximately like this should work

object identityMacro {
  def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
    import c.universe._
    val inputs = annottees.map(_.tree).toList
    val (annottee, expandees) = inputs match {
      case (param: ValDef) :: (rest @ (_ :: _)) => (param, rest)
      case (param: TypeDef) :: (rest @ (_ :: _)) => (param, rest)
      case _ => (EmptyTree, inputs)
    }
    val stringToPrint = (annottee, expandees).toString
    c.Expr[Any](q"""
    println($stringToPrint)
    $expandees
    ()
    """)
  }
}