使用def宏实现抽象方法

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满足特质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

你是否真的需要将 (a: AImpl).foo() 的任何调用都作为宏在使用时进行扩展,还是只需要生成(普通)foo() 方法的内容? - gourlaysama
2个回答

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

好的,是的,我发现我需要两次编译运行。此外,我认为我明白了为什么我不能使用宏“实现”抽象方法——因为宏调用会用宏体“替换”方法。也就是说,在您的代码中,方法bar()将消失,以便def foo(): Unit = {}保留。所以是的,我可以选择这个转发器,或者我使用组合(从宏内部实现整个特质)。 - 0__

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设置子项目,否则这些文件无法同时编译 :-/

1
哦,我现在明白你想做什么了。宏确实是在调用时扩展和内联的,因此它们无法实现或覆盖抽象方法,因为它们实际上并不产生方法。 - gourlaysama
我需要使用sbt设置一个子项目,否则这些文件将无法同时编译。这是因为“最常见的原因是您不能在定义宏实现的同一编译运行中使用它们”。 - Kevin Meredith

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接