Can someone explain to me why Lisp macros are better than, say, the C preprocessor? (I don't know much about Lisp, and I suspect that Lisp macros are much more elegant; I just want a concrete explanation from someone who knows what he's talking about.) -- AdamSpitz
Sure. In C, the best you can do is textual substitution. This allows you do to things like
#define MAX(x,y) ( ((x) > (y)) ? (x) : (y) )taking great care to put in all those extra parentheses. That's about the level that I, personally, can "rewrite syntax" using the C preprocessor. Note that you do not have access to C at this point, only to this mini-language provided by the preprocessor. It allows you do do things like splicing tokens in, with our without the double quotes, etc. But that's about it.
In Lisp, your macro can perform any computation on its arguments whatsoever. It can decide which arguments to evaluate, and whether they should be evaluated zero times, once, or any other number of times. If these arguments have side effects, this could be important. This features allows you to add first-class syntactic elements to the language, i.e. elements which are on par with the built-ins of the language. (In fact, many of the built-ins are macros, e.g. SETF, WITH-OPEN-FILE, etc. The number of special forms, i.e. those who have special cases for the lisp interpreter/compiler, is actually very small in lisp, and many are obscure low-level things not used very much in day-to-day programming. The obscure ones include BLOCK, GO, PROGV, SETQ. The well known ones are LET, PROGN, IF, UNWIND-PROTECT etc.
What do typical, real life macros used by lispers look like? they may look like this:
(map-over-active-allocators (alloc :continue-on-error t) (log-message :info "~&Notifying allocator ~A" alloc) (my-remote-operation alloc))Which might be defined as follows:
(defmacro map-over-active-allocators ((var &key (continue-on-error t)) &body body) "Execute BODY over each active allocator in a context where VAR is bound to each one in succession. If BODY raises an error, the error is ignored and the form is attempted over the next allocator if CONTINUE-ON-ERROR is T, otherwise, the error is re-thrown and BODY may not be executed on all allocators" `(loop for allocator over (currently-active-allocators) do (let ((,var allocator)) (handler-case (progn ,@body) (error (err) (when ,continue-on-error (error err)))))))This code omits some issues of hygiene (e.g. continue-on-error is evaluated multiple times) for pedagogical purposes.
There - that help? :-) -- AlainPicard
Beautiful. ThankYou. :) -as
Also see CeePlusPlusTemplatesCommonLispMacrosComparison.