The implementation difficulties you face are in part the reason why macros in 2.10 are not general enough. They look very challenging and even fundamental, but I'm optimistic that they can be eventually defeated. Here are some of the tricky design questions:
1) How do you know that the function you are calling is the right insertTree
? What if the user has written his own function named insertTree
- how do you then distinguish a magic call to your special function and a normal call to a user-defined function? To be sure you would need to typecheck the reference to the function. But that's not exactly easy (see below).
2) How exactly do you evaluate the createFooTree(...)
call? Just as before, you would need to typecheck the createFooTree
part to find out what it stands for, which isn't easy.
3) And then there's one more issue. What if createFooTree
is defined in one of the files you're currently compiling? Then you would somehow need to separate it and its dependencies from the rest of the program, put it into a different compilation run, compile it and then call it. And then, what if compilation of the function or one of these dependencies leads to a macro expansion, which is supposed to mutate some global state of the compiler. How are we going to propagate it to the rest of the program?
4) I'm talking about typechecking all the time. Is that a problem? Apparently, yes. If your macros can expand anywhere into anything, then typechecking becomes really tricky. For example, how do you typecheck this:
class C {
insertTree(createFoo(bar)) // creates `def foo = 2`, requires `bar` to be defined to operate
insertTree(createBar(foo)) // creates `def bar = 4`, requires `foo` to be defined to operate
}
5) Good news, though, is that you don't have to use scala.reflect.runtime.universe.Tree
. You could have createFooTree
dependently-typed: def createFooTree[U <: scala.reflect.api.Universe with Singleton](u: Universe): u.Tree
. This, or the approach with scala.reflect.macros.Context
we use in Scala 2.10. Not very pretty, but solves the problem of universe mismatch.
As a bottom line, my current feeling is that macros in a statically typed language (especially, in an object-oriented language, since OO brings an amazing bunch of ways for pieces of code to depend on each other) are really tricky. A robust model for typed macros modifying arbitrary fragments in the program being compiled is yet to be discovered.
If you wish we could have a more detailed discussion via email. We could also collaborate to bring the idea of proper macros to fruition. Or alternatively if you could share your use case, I could try to help with finding a workaround for your particular situation.