5

通过 def 宏实现抽象方法似乎是不可能的:

import scala.reflect.macros.Context
import language.experimental.macros

trait A {
  def foo(): Unit
}

object AImpl {
  def fooImpl(c: Context)(): c.Expr[Unit] = {
    import c.universe._
    c.Expr[Unit](reify().tree)
  }
}
trait AImpl extends A {
  def foo(): Unit = macro AImpl.fooImpl
}

这失败并出现以下错误:

[error] .../A.scala:17: overriding method foo in trait A of type ()Unit;
[error]  macro method foo cannot override an abstract method
[error]   def foo(): Unit = macro AImpl.fooImpl
[error]       ^

如果我删除extends A它会编译。但显然我想AImpl满足 trait A。如何解决这个问题?


另一个尝试:

trait AImpl extends A {
  def foo(): Unit = bar()
  def bar(): Unit = macro AImpl.fooImpl
}

给出新错误:

[error] macro implementation not found: bar (the most common reason for that is that
  you cannot use macro implementations in the same compilation run that defines them)
[error] one error found
4

2 回答 2

4

我不确定这是否正确,所以请添加一个额外的权威答案。

我才刚刚开始了解 def 宏是如何工作的。问题中的错误假设是def bar(): Unit = macro ...实际上创建了一个运行时bar 方法。相反,它创建了......好吧,一个,所以对该宏的任何调用都只是在表达式中拼接。

所以我看到两件事。返回类型都变为c.Expr[DefDef],但我不确定这是否可能,而且可能还有很多工作要做。第二种选择是生成整个特征,例如作为匿名类:

import scala.reflect.macros.Context
import language.experimental.macros

trait A {
  def foo(): Unit
}

object AImpl {
  def body: A = macro bodyImpl
  def bodyImpl(c: Context): c.Expr[A] = {
    import c.universe._
    val r = reify { new A { def foo() { println("schoko" )}}}
    c.Expr[A](r.tree)
  }
}

然后你有组合而不是mixin:

object AHolder extends App {
  val bar: A = AImpl.body

  bar.foo()
}

最糟糕的是,我需要用 sbt 建立一个子项目,否则这些文件不会同时编译:-/

于 2013-06-17T15:32:09.133 回答
4

你确定你用先编译AImpl后的宏进行了测试吗?

使用像您第二次尝试这样的转发器方法似乎有效(使用 2.10.2):

// first compilation run

import scala.reflect.macros.Context
import language.experimental.macros

trait A {
  def foo(): Unit
}

object AImplMacros {
  def fooImpl(c: Context)(): c.Expr[Unit] = {
    import c.universe._
    c.Expr[Unit](reify().tree)
  }
}

// second compilation run

trait AImpl extends A {
  def foo(): Unit = bar()
  def bar(): Unit = macro AImplMacros.fooImpl
}

// compiles and runs:

scala> val a = new AnyRef with AImpl
a: AImpl = $anon$1@59225446

scala> a.foo

scala> a.bar
于 2013-06-17T15:28:51.667 回答