Скажем, у меня есть трейт Foo
, который я инстанцирую с начальным значением i
.
val foo = new Foo(6) // class Foo(i: Int)
и позже я звоню secondMethod
, который, в свою очередь, звонит myMacro
foo.secondMethod(7) // def secondMethod(j: Int) = macro myMacro
тогда как myMacro
может найти начальное значение i
(6)?
Мне не удалось нормальное отражение компиляции с использованием c.prefix
, c.eval(...)
и т. д., но вместо этого я нашел решение с двумя проектами:
Проект Б:
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)
}
Проект А (зависит от проекта Б):
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})
}
Мы можем создать Foo
и установить значение i
либо с помощью обычного создания экземпляра, либо с помощью макроса, такого как makeFoo
. Второй подход позволяет нам настроить Foo
во время компиляции в первой компиляции, а затем во второй компиляции дополнительно настроить его реакцию на ввод (в данном случае i
)! Каким-то образом мы получаем «мета-мета»-возможности (или «патафизические»-возможности ;-)
Обычно нам нужно иметь foo в области видимости для самоанализа i
(например, c.eval(...)). Но, сохранив значение i
внутри объекта Foo
, мы можем получить к нему доступ в любое время и создать экземпляр Foo
где угодно:
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))
}
Мой вопрос
Могу ли я найти i
внутри моего примера макросов без этой двойной хакерской компиляции?