You're right,
are not so interesting because new definitions are not visible from outside the macro expansion. These annotations are just examples how to write macro annotations in Scala 3. To make them interesting, new definitions should be used in some way inside the macro expansion.
But look at @memoize
and @equals
from Macro Annotations in Scala 3. They are more interesting because new definitions are actually used inside the macro expansion. So sometimes macro annotations can be useful in Scala 3.
Although in Scala 3 macro annotations are indeed much more restrictive than those in Scala 2 (and macros generally are more restrictive in Scala 3 than in Scala 2 e.g. there are no c.eval
, c.parse
, quasiquotes etc.). So code generation with Scalameta (and Semanticdb), Scalafix, compiler plugins etc. are still necessary in cases when macros or macro annotations are not enough. You can read discussion in https://contributors.scala-lang.org/t/scala-3-macro-annotations-and-code-generation/6035 with motivation why to introduce macro annotations in Scala 3. A quote from there:
@odersky:
I believe the way it is intended, a macro annotation can check that a class conforms to a certain schema, but cannot generate definitions that makes it conform. So if the macro gets updated, the new check might fail and another action to fix it might be proposed.
It’s similar to @tailrec
. No magic, just an assertion that the annotated construct has certain properties.
I thought that you can do already do the same thing by overriding/wrapping a definition with a Macro.
What is it that you can do with a MacroAnnotation
that you cannot do with a Macro?
Well, that's the thing that you do not need to define a new macro wrapper every time you want to instrument another definition, it's enough to annotate it with the same macro annotation.
Moreover, new definitions are not visible from outside the macro expansion statically. In bytecode they are present. You can call them with runtime reflection. For example for @mainMacro
from Macro Annotations in Scala 3
import Macros.mainMacro
import scala.annotation.experimental
@experimental
object App:
@mainMacro def Test(): Unit = println("macro generated main")
//scalac: List(class Test extends java.lang.Object {
// def main(args: scala.Array[scala.Predef.String]): scala.Unit = App.Test()
//}, @Macros.mainMacro def Test(): scala.Unit = scala.Predef.println("macro generated main"))
// new App.Test().main(Array[String]()) // doesn't compile: type Test is not a member of object App
val clazz = Class.forName("App$Test")
clazz.getMethod("main", classOf[Array[String]])
.invoke(clazz.newInstance(), Array[String]())
// macro generated main