Update: For some reason WP disabled comments when I first posted this. Fixed now.
There’s a thread on c.l.l going, in which people try to find a reason macros haven’t caught on for the past 30 years, despite their immense usefulness. One of the cited arguments was this statement by one of the men in charge of Java process:
The advantages of Java is that it easily serves as a lingua franca - everyone can read a Java program and understand what is going on. User defined macros destroy that property. Every installation or project can (and will) define its own set of macros, that make their programs unreadable for everyone else. Programming languages are cultural artifacts, and their success (i.e., widespread adoption) is critically dependent on cultural factors as well as technical ones.
My claim is that this is about as true as C++’s approach to performance, in which people copy things all the time, because that’s what manual memory management makes them do. Just consider the “design patterns” omnipresent in Java. What is a macro, if not a structured transformation of source, following certain pattern? Of course, I’m saying nothing new here, but I just stumbled upon a particularly good demonstration of why “macros decrease legibility of code” is utter nonsense. Consider this snippet:
(restart-bind ((retry (lambda () (throw 'retry nil)) :report-function (lambda (stream) (write "Retry reading the record." :stream stream)))) (catch 'ok (loop do (catch 'retry (throw 'ok (unless (read-record) (error "Record not available.")))))))
Just try analysing it, how fast can you find out what exactly it does? Aren’t braided catch/throws fun? Now consider the same snippet using a macro:
(with-retry (:report "Retry reading the record.") (unless (read-record) (error "Record not available.")))
Not only is the code 4x shorter and uses idiomatic naming conventions and code structure, the macro also comes with a docstring and parameters to let you easily influence the working. And most importantly, you only have to understand it once. Whereas in Java every time you encounter a similar-looking snippet, you have to analyse it to see if it’s actually the same thing, or subtly different.
So, to sum up with a checklist, the macroless code is:
- More verbose
- Really complex and confusing
- Hard to spot and identify reliably
- Not documented
- Must be re-invented each time you need it
- Does not ultimately prevent convoluted code from coming up. If your code needs retry functionality, then so it does, and no amount of “ours is a simple language for the masses” can change it. Though if you make it sufficiently painful, people will probably find ways to dillute the apparent complexity amongst modules, only compounding the problems, and/or pretend they don’t really need their program correct, because it’s such a massive PITA to do it.
Now, to be fair, this is not to say that macros can’t be hard — very often they are, because you generally use macros to solve problems that would be even harder without them. You don’t deny surgeons access to endoscopy because they might not be skillfull enough to handle it — instead you make sure they are or have to find another trade. There’s no reason to treat programmers differently.
Saying “macros make code unreadable” is like saying “race bets can only be done on manure-covered floor, or else you won’t know you’re dealing with horses”.