It seems to be impossible to implement abstract methods via def macros:
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
}
This fails with the following error:
[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] ^
If I remove the extends A
it compiles. But obviously I want AImpl
to satisfy trait A
. How to fix this?
Another try:
trait AImpl extends A {
def foo(): Unit = bar()
def bar(): Unit = macro AImpl.fooImpl
}
Gives new error:
[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
Are you sure you did test with the macro compiled first and AImpl
later?
Using a forwarder method like your second try seems to work (with 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
I'm not sure if this is correct, so please add an additional authoritative answer, please.
I'm only starting to understand how def macros work. The wrong assumption in the question is that def bar(): Unit = macro ...
actually creates a runtime bar
method. Instead it creates... well, a macro, so any call to that macro just splices in the expression.
So I see two thing. Either the return type becomes c.Expr[DefDef]
, but I'm not sure that's even possible, and probably it's a lot more work. The second option is to generate the whole trait instead, e.g. as an anonymous class:
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)
}
}
Then instead of mixin, you have composition:
object AHolder extends App {
val bar: A = AImpl.body
bar.foo()
}
The big suck is, I need to set up a sub project with sbt because otherwise these files don't compile at the same time :-/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With