5

I must integrate some macros in a project which is using a cake-pattern. That pattern allowed us to avoid zillions of imports, among other advantages, so we would like to keep it. Now, we are facing a problem with some experimental macros we have been testing outside the trunk. First, let's show a dummy system named Cake:

trait APiece {
  class A
}

trait BPiece { this: APiece => 
  def aMacro(a: A): Unit = () /* macro ??? */
}

trait CPiece { this: APiece with BPiece =>
  def aMacroInvoker = aMacro(new A)
}

class Cake { this: APiece with BPiece with CPiece => }

APiece defines a class, BPiece is supposed to be a macro which uses the APiece defined class, and finally, CPiece invokes the macro. I said that BPiece was supposed to be a macro since I was unable to code an implementation for it. I have tried several ways but I always crash with the following error:

"macro implementation must be in statically accessible object"

Reading the macros code one can guess that it is neccesary to enclose the macro in a static module. Is there any way to deploy a macro which uses the system structures?

neutropolis
  • 1,884
  • 15
  • 34

1 Answers1

4

Luckily there's an easy solution to your problem.

But first, let me give some retrospective. In the very first prototype, macros were defined like that: def macro aMacro(a: A): Unit = .... One of the major breakthroughs we've had when preparing a SIP is separating macro definitions (public faces of macros) and macro implementations (tree transformers that host macro logic). It took me a while realize how cool this is, but now I'm glowing with joy every time when I write a macro declaration.

So, back to your question. Sure, macro implementations have to be statically accessible (otherwise, compiler won't be able to load and invoke them during the compilation). However macro definitions don't have this restriction, so you can write the definition like that:

trait BPiece { this: APiece => 
  def aMacro(a: A): Unit = macro Macros.aMacro
}

Macro implementation that is referred to from the definition can be put into whatever object you wish, even into a different compilation unit.

The only missing piece of puzzle is how we're going to refer to A from the implementation, because A is defined inside a cake. The simplest way would be to make aMacro generic and rely on type inference:

(update: to make this example work in 2.10.0-M7, you need to replace c.TypeTag with c.AbsTypeTag; to make this example work in 2.10.0-RC1, c.AbsTypeTag needs to be replaced with c.WeakTypeTag)

trait BPiece { this: APiece =>
  def aMacro[A](a: A): Unit = macro Macros.aMacro[A]
}

object Macros {
  def aMacro[A: c.TypeTag](c: Context)(a: c.Expr[A]): c.Expr[Unit] = c.literalUnit
}

This won't let you use reify, though, because for a macro implementation A is just a type parameter without any members. There are also going to be problems if you'll want to return something cake-specific from the macro, but let's deal with them when they arise. Please, submit follow-up questions if you need to.

Eugene Burmako
  • 13,028
  • 1
  • 46
  • 59
  • 2
    I don't think that solves his problem, and I don't think anything will. The essence of the cake pattern -- as you should know! :-) -- is being able to choose the layers you want in the "client" code. If `Macros` is static, you can't switch it at will. You could switch source files or class files, but you can't write code that say "this will use macros from here, and that will use macros from there". – Daniel C. Sobral Jun 28 '12 at 19:14
  • Great, I think new problems will arise, but I can go on with this solution by now. Thank you very much! Daniel, our system is not going to be really (cake-pattern) pure with macros. We just will allow the client to decide if he want to use them or not. So, I hope this to be good enough. – neutropolis Jun 29 '12 at 07:18
  • @DanielC.Sobral I though the idea was to declare a macro inside a cake. If you have different layers that define macros referring to different macro implementations, you can switch between them as you wish and get different behavior. – Eugene Burmako Jun 29 '12 at 09:03
  • 1
    Oh but there's a problem in a sense that you cannot have an abstract method overriden by a macro. So it's impossible to define a public swappable macro. – Eugene Burmako Jun 29 '12 at 09:07