I recently had occasion to use Boo’s macro feature in anger. In particular, I wanted to build something like “with” in JavaScript. Those who aren’t familiar with Boo’s macros might think this couldn’t be that hard. I mean, there’s an example that does this right on the page, isn’t there?
Ironically, the example actually demonstrates the limitations of boo’s macros, rather than their power. Let’s start with the requirement to prepend method names with an underscore. It shouldn’t be too hard to make it use a dot just like VB, should it? Well, no, an expression that began with a dot wouldn’t be syntactically valid. So here we have the first major restriction: you can’t create new syntactic structures, you can only abuse the ones that are already there.
That isn’t what I wanted to do, however, I wanted JavaScript’s with statement. I didn’t want to prepend anything to the method name, I just wanted to use the method name and have it resolve to the instance’s method. Shouldn’t be too hard, just look up the instance’s type’s method names and rewrite the appropriate expressions. Only you can’t do that: they’re called “syntactic” macros for a reason: there’s no semantic information, including type, available at this point. You’ll note that this has a knock-on effect on the using macro. The check that the subject of the macro implements IDisposable is at runtime. In C#’, it’s at compile time. Probably not the end of the world, but I think people would be surprised if “using (3) {}” compiled in C#.*
There are other ways to achieve the effect, you could push delegates into the execution scope, but supporting overloading would be hairy in the least. You could do an import, but that will only work with static methods. In general terms, the syntactic macros don’t give you enough unless you’re prepared to be really ugly and the static type system takes away flexibility you’d have had in a proper dynamic language like JavaScript, Python or Ruby.
Now, I’ve had some reservations about the conceptual value of macros anyway, and this has firmed up my opinions. Macros are only useful if they’re relatively easy for the user to understand intuitively what they do. It’s interesting to look at Binsor here. For the most part it presents you with a view of the world that suggests the mapping from the DSL to Castle Windsor is simple. However, once you start digging under the hood, it gets unpredictable. To this day, I have no idea how to configure a facility; I’m using code ripped off the internet for each one. I remember it took me a day to figure out how to set a config value on a component. Ultimately, it’s a pretty big code base for something that can be 90% replaced with 5 lines in a blog post. (Not 100% yet, though. I’ll let you know when I’ve done that. 🙂 )
Obviously, all tools have advantages and disadvantages. But with Boo, I’m just not sure it’s got an advantage over IronPython I care about. I eventually ended up adopting pretty much the “with” code in the sample, because I don’t believe it can be usefully improved.