对定义宏的对象成员的宏访问

时间:2013-06-22 18:30:27

标签: scala scala-macros

说我有一个特征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.prefixc.eval(...)等进行正常的编译反射后,我没有成功,而是找到了一个双项目解决方案:

项目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)
}

项目A(取决于项目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})
}

我们可以创建Foo并使用正常实例化或像i这样的宏设置makeFoo值。第二种方法允许我们在编译时在第一次编译中自定义Foo,然后在第二次编译中进一步自定义其对输入的响应(在这种情况下为i)!在某种程度上,我们获得“元元”功能(或“pataphysic” - 能力; - )

通常我们需要在范围内使用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))
}

我的问题

如果没有这种双重编译hackery,我可以在我的示例宏中找到i吗?

1 个答案:

答案 0 :(得分:1)

事实证明,在宏中访问Foo的成员很容易,而不必采用双重编译。以下是我们的示例宏如何访问i

的值
    val i = c.Expr[Int](Select(c.prefix.tree, TermName("i")))
    reify(i.splice * j.splice)

准确地说,i这里实际上是Expr,我们可以在reify 中拼接和使用数字值。

所以是的,可以在宏(process)内部访问定义宏的对象(i)的成员(Foo)(Foo )在单个编译中(由“已定义”表示我使用macro关键字的位置):

object compilation {
  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})

  def process(c: Context)(j: c.Expr[Int]): c.Expr[Int] = {
    import c.universe._
    // Foo.i accessed inside macro
    val i = c.Expr[Int](Select(c.prefix.tree, TermName("i")))
    reify(i.splice * j.splice)
  }
}

trait Foo {
  val i: Int
  def apply(j: Int) = macro compilation.process
}

object Test extends App {
  import compilation._

  val foo1 = new Foo {val i = 6}
  Console println foo1(7) // 42

  val foo2 = makeFoo(6)
  Console println foo2(7) // 42

  Console println makeFoo(6)(7) // 42
}

我欠Francesco Bellomi / Eugene Burmako关于Scala用户列表{@ 3}}的问题/答案的解决方案。

作为旁注,我们不一定需要使用c.eval(...)从某些无类型的Expr获取实际值 [正确地说它就像那样?] Tree。在大多数情况下,我们应该很好地将值包装在Expr 中,因为我们可以将用作值,将其拼接在reify中并执行所有操作那里用(splice-)值计算!