I am currently designing a DSL using Scala macros (including macro paradise for macro annotations) to simplify the user-visible interface. I am using a few features of macros that I am unsure if they are considered 'acceptable.' (I apologize if this is the wrong venue. In my searches, I was finding more posts about macro features in scala-internals than scala-users.)
Is it acceptable to have a macro override another macro?
This is requisite because, in the subclass, I wish to refine the type. In each case, the macro is transforming a 'user-visible, external' function call with some fields to an internal one with more fields. This is particularly helpful for adding implicits without using a second parameter list (as this tends to break 'chained' applies). Further, the macro can trivially use enclosingPosition to see precisely in the user code where the function was invoked.
I currently do this (vastly simplified):
abstract class Parent {
// external->internal API
def do_modifySelf(myImplicit: SomeClass, debug_info: String): Parent
// external API
def modifySelf: Parent = macro MacroImpl.toModifySelf
}
class Child {
// external->internal API
def do_modifySelf(myImplicit: SomeClass, debug_info: String): Child = ??? // implementation here
// external API
override def modifySelf: Child = macro MacroImpl.toModifySelf
}
object MacroImpl {
import scala.reflect.macro.blackbox.Context
def toModifySelf(c: Context): c.Tree = {
import c.universe._
def myThis = c.prefix.tree
def emtype = tq"_root_.mypackage.SomeClass"
val location = Literal(Constant(c.enclosingPosition.toString))
q"$myThis.do_modifySelf(implicitly[$emType], $location)"
}
}
Currently, it works amazingly. e.g. the following 'user code' typechecks and expands as expected:
val myParent: Parent = new Child
val myChild: Child = new Child
myParent.modifySelf: Parent
myChild.modifySelf: Child
myChild.modifySelf: Parent
//myParent.modifySelf: Child // fails to typecheck, as desired
Referencing
https://groups.google.com/forum/#!topic/scala-internals/otRPlIRoEqs, it seems that everything is sound as long as a macro only ever overrides another macro and the overriding type is a subtype although I have not thoroughly considered the potential pathological cases. Actually, the override macro is seems only necessary to remind the type inferer that the type of the doModifySelf is changing. The actual macro that is applied does not change at all. (To some degree, I wonder if using a whitebox context and some other adjustments would allow me to avoid the macro overrides. Of course, if that works, I wonder if this is an appropriate use of whitebox macros.)