What Do Macros Actually Give You: Reader Response
Posted: 4 years ago (2007-11-27 18:55:20 UTC ) / Updated: 2 years ago (2009-06-01 22:27:36 UTC )
Imported from WordPress
Originally posted on 2007-11-27 18:55:20
After I wrote part one, the anonymous reader FooBar posted an excellent reply in the comments. I'd like to respond to the points he made there.
First off, I'd like to make it clear that I'm not talking about what optimizations macros can give you. Macros do often give good optimizations, but I'm not prepared to do the amount of profiling required to seriously address this topic. Macros can be a great way to avoid using closures, for instance, which is useless to know unless you know how fast macros compared to closures. You'd want to know in at least one LISP interpreter, and how fast closures are versus other workarounds in your language of choice. I'm not prepared to answer those questions properly, even for my dynamic language of choice (Ruby). I may do so at some point in the future.
I am trying to answer questions of expressiveness of methods other than macros versus expressing the same thing in macros. That's a subjective question, but I think raising it so that people can judge for themselves is basically useful.
He points out that macros can be used to create new control structures (for example, LISP's LOOP) and new semantics. Both of these are normally accomplished with the "fill in the blank" code in languages like Ruby, Perl and Python that have eval, first-class method objects and closures, but no macros. "Fill in the blank" code is discussed in part one. He also mentions that macros can expand symbols into code, which is even more literally "fill in the blank" code. The ITERATE construct for LISP is an example of this kind of macro coding, and its overall form and semantics should be familiar to most Ruby users. This is the kind of "build a control structure out of blocks" coding that Ruby excels at.
Macros also allow execution of code at compile-time, which is a neat trick. I still have to say, though, that Perl has the best facility for this that I've ever seen. I miss it in other languages. In Perl, code sections inside a BEGIN {} section are executed at compile time, and the BEGIN section is replaced with their value. I'm sure there are ways to fake this in other languages, but I can't think of a better, or less complex, way to do it. LISP macros use a fairly obscure method, which foobar mentions, to determine whether blocks are executed at one time or another. Ruby has a fairly similar facility with class_eval() versus regular eval(). It's obscure and hard to keep track of in Ruby, too :-)
However, macros can also analyze a piece of code and do compile-time optimization. This is a really, really good trick which can't be replicated in macro-less languages. He mentions the SCREAMER macro library for LISP which makes extensive use of this facility to implement PROLOG-style backtracking variables in a LISP setting. You can do that without macros, but macros allow for a lot more optimization in this case. So there's a trick that we simply can't do without macros or something like them. The SERIES macro library for LISP does tricks with iteration on (potentially infinite) series in a similar way, though it's easier to work around this in other languages.
I'd also like to point out that I was wrong about there being no way to parse Ruby in Ruby, or to get at Ruby parse trees in general. There's a ParseTree rubygem that will access Ruby's internal parse tree, and a lovely tool called Heckle that uses it to take your code apart and make sure your tests fail at all the right times.
Edit: Another blogger, Eric Kidd, has addressed a similar topic with respect to Ruby and macros elsewhere. He's also worth a read if you're interested in where LISP macros give you power that Ruby doesn't.
