Lisp Macros - what’s the fuss about?
I've been working through the Seibel book and I'm quite keen to draw comparisons with other languages (especially python+java), since that's what first attracted me to learn more in the first place. I thought it would be better to blog as I went along (while I'm motivated) rather than when I'd got to the end (when I might not be bothered). The obvious caveat being that I'm only half way through the book so I may have mis-understood something.
Anyway, here's my take on lisp macros:
The lisp language lacks syntactic sugar - you've basically got brackets and spaces to create structure and that's it. The lack of operators to define structure means you end up with a load of verbose text even if you're doing basic stuff.
E.g. Consider accessing an attribute of an object: Java has '.' hardcoded into the language grammar to mean 'attribute of object', whereas lisp has to make 'slot-value' function calls all over the shop:
In Java:
if (account.balance < account.minimum_balance){
account.balance -= account.balance * 0.01;
}
In Lisp:
(when (< (slot-value account 'balance) (slot-value account 'minimum-balance))
(decf (slot-value account 'balance) (* (slot-value account 'balance) .01)))
To combat this verbosity, lisp has macros.
Lisp macros are vaguely similar to C macros in that they allow the programmer to rewrite the code before it gets compiled/run. Unlike C macros, which operate on text and perform limited operations, lisp macros operate on parsed s-expressions and can use the full power of the lisp language to do the transformation (including utilising other functions and macros). Since lisp programs are themselves list datastructures, translating some new invented construct into vanilla lisp s-expressions is pretty straightforward.
To illustrate the power, consider the verbose OO lisp code in the above example. In order to allieviate the 'slot-value ...' redundancy somebody has implemented a 'with-slots' construct which factors out the 'slot-value' calls and allows you to use the variables names directly. Now the code looks like:
(with-slots (balance minimum-balance) account
(when (< balance minimum-balance)
(decf balance (* balance .01))))
In this example the args and body of the construct are passed to the with-slots macro which then generates the vanilla lisp code in the original example.
From my limited knowledge of lisp, it looks like the entire OO system is implemented as a bunch of macros - including class declarations etc.. This is quite impressive when you consider that the language itself was invented before object orientation was conceived.
This ability to add new constructs to the language appears to run deep in lisp, with proponents calling it the 'programmable programming language'. The litmus test for me will be whether this style translates into better abstractions and overall productivity in my software development.
[1] which is part of the default library