When defining a macro, there are always two functions created: user facing, compiler facing. These two functions should always be mirror images of each other (name, input, output all match) with one exception; compiler facing wraps types.
So what is the basic structure of a macro?
User facing API is very similar to writing a normal function but the implementation is missing and replaced by the keyword macro
which references the compiler facing API.
def printparam(param: Any): Unit = macro printparam_impl
As stated above, the compiler facing API must be a mirror image of the user facing API, but wrap input/output in compiler types.
def printparam_impl(c: Context)(param: c.Expr[Any]): c.Expr[Unit] = {
import c.universe._
reify { println(param.splice) }
}
Here we see the second group of params looks a lot like the params from the user facing one:
param
c.Expr[Any]
and user was Any
So whats up with the curried API? The macro system was written following the cake pattern. Because of this, you will need access to the Context
of the compiler, and need to reference types from its universe
. All macro compiler functions will have the same boilerplate of taking the Context
in the first param group, and importing its universe
as its first statement. This setup has one big drawback to it...
Scala's compiler will load the compiler function into its code, but nothing else. This means that when the compiler runs your function, you won't have access to functions defined outside. This is a big issue when building bigger macros, so a pattern has come up of not using macro functions but macro bundles.
When defining the user facing API, you can tell it that the macro can be found inside a given class (Foo.bar
). This lets you define the Context
as a param to the class and import its universe
one time. Each function inside this class will look like the function above, but with the first param group omitted.
There is one issue with this though, macro bundles was only added in scala 2.11. If you want support for this in 2.10 you will need a compiler plugin called Macro Paradise. Need to test it, but I believe that users of the macro code that depends on macro paradise will also need to include macro paradise. This still needs to be tested.