Macro access to members of object where macro is defined
- by Marc Grue
Say I have a trait Foo that I instantiate with an initial value
val foo = new Foo(6) // class Foo(i: Int)
and I later call a second method that in turn calls myMacro
foo.secondMethod(7) // def secondMethod(j: Int) = macro myMacro
then, how can myMacro find out what my initial value of i (6) is?
I didn't succeed with normal compilation reflection using c.prefix, c.eval(...) etc but instead found a 2-project solution:
Project B:
object CompilationB {
def resultB(x: Int, y: Int) = macro resultB_impl
def resultB_impl(c: Context)(x: c.Expr[Int], y: c.Expr[Int]) =
c.universe.reify(x.splice * y.splice)
}
Project A (depends on project B):
trait Foo {
val i: Int
// Pass through `i` to compilation B:
def apply(y: Int) = CompilationB.resultB(i, y)
}
object CompilationA {
def makeFoo(x: Int): Foo = macro makeFoo_impl
def makeFoo_impl(c: Context)(x: c.Expr[Int]): c.Expr[Foo] =
c.universe.reify(new Foo {val i = x.splice})
}
We can create a Foo and set the i value either with normal instantiation or with a macro like makeFoo. The second approach allows us to customize a Foo at compile time in the first compilation and then in the second compilation further customize its response to input (i in this case)! In some way we get "meta-meta" capabilities (or "pataphysic"-capabilities ;-)
Normally we would need to have foo in scope to introspect i (with for instance c.eval(...)). But by saving the i value inside the Foo object we can access it anytime and we could instantiate Foo anywhere:
object Test extends App {
import CompilationA._
// Normal instantiation
val foo1 = new Foo {val i = 7}
val r1 = foo1(6)
// Macro instantiation
val foo2 = makeFoo(7)
val r2 = foo2(6)
// "Curried" invocation
val r3 = makeFoo(6)(7)
println(s"Result 1 2 3: $r1 $r2 $r3")
assert((r1, r2, r3) ==(42, 42, 42))
}
My question
Can I find i inside my example macros without this double compilation hackery?