4 ms·
> I've heard this repeatedly over the years, but the explanation unfortunately always stops right there. Lisp macros are something like C macros, chainsaws, hy
by mschaef 10y ago
> I've heard this repeatedly over the years, but the explanation unfortunately always stops right there.
Lisp macros are something like C macros, chainsaws, hydrochloric acid, or anything else that's powerful-but-dangerous. This is to say that sometimes they are the one tool you have to use, but often they are unnecessary and should be avoided.
Coming up on ten years ago, I wrote this blog post on one of the dangers of Common Lisp style macros: http://www.mschaef.com/blog/tech/lisp/defmacro-coupling.html http://www.mschaef.com/blog/tech/lisp/defmacro-coupling.html
Put succinctly, the problem I write about in the blog post is that macros are essentially always inlined into the output of the compiler. This has the effect of more tightly coupling the modules together than is evident in the surface syntax. (Note that macro invocation sites are syntactically indistinguishable from function invocation sites, which makes this problem worse.)
The upshot of this is what you'd expect: the extent of the logic encoded in macros should be minimized, with the macros translating the code pretty much straight away into something built on more functional abstractions.
This is not to say that Macros aren't useful... sometimes you _have_ to use them to achieve a goal. Just that they probably aren't as big a deal as might be expected given the amount of 'press' they get.
- kazinator 10y agoAll computer code is a dangerous tool that is often unnecessary and should be avoided if possible. A macro is no more or less dangerous than a function, class, variable, module, or anything else. C macros have the issue that even when everyone involved in the creation and use of a C macro understands its pifalls, those pitfalls cannot be removed from the macro. For instance, a certain C macro might evaluate some expression twice. Everyone knows that this is dangerous, but there isn't any way to fix it. They just document it. ISO C itself says that getc may evaluate its argument multiple times; thus don't do things like getc(stream_array[i++]) unless you remove the macro definition wth #undef. Lisp macros do not have issues that are unfixable in this way. Sometimes they have issues that are difficult, though not impossible. Usually that occurs when, to be perfect, the macro would have to do a full-blown code walk. Macros are written that do code walks (for instance the iterate macro). > Put succinctly, the problem I write about in the blog post is that macros are essentially always inlined into the output of the compiler. This has the effect of more tightly coupling the modules together than is evident in the surface syntax. (Note that macro invocation sites are syntactically indistinguishable from function invocation sites, which makes this problem worse.) Before you apply macros, you need a well-designed (and documented, and versioned!) API against which the macros will write the code. If all you care about is what the macro syntax looks like and don't put any design into how the expansion works (beyond just massaging it so that it somehow works), then you may run into problems. Macros don't introduce any problems that writing the same code by hand against the same API's wouldn't introduce. If someone has to write the code, I don't see how you can get around it: it's either going to be a human, or a macro.
- mschaef 10y ago> a certain C macro might evaluate some expression twice. Everyone knows that this is dangerous...ISO C itself says that getc may evaluate its argument multiple times; ... Lisp macros do not have issues that are unfixable in this way. If your macros is 'fixed' to emulate function call semantics by evaluating its arguments only once, then maybe a function is a more appropriate abstraction in the first place. The whole point of macros is that they let you break the rules of function call application in hopefully useful and predictable ways. Another way to look at it is that repeatedly evaluating an argument is what you do NOT want for a macro like 'getc', but probably what you DO want for a macro like 'repeat'. The danger lies in the fact that it's hard to tell the difference when looking at a call site in isolation. Whether or not that danger is an acceptable risk is, of course, situation dependent.
- kazinator 10y agoBy the way, some 18 years ago, I came up with a system for catching the use of expressions with side effects in C macros. Basically, I introduced an API that you could use in your macro definitions to identify insertions of expressions which would cause problems if containing side effects. This API, at run-time, would parse the expressions, analyze them for side effects, and diagnose problems. (It would also cache the results for faster execution of the same macro site.) All the programmer has to do is achieve run-time coverage to catch all the problems. We could define a getc-like macro such that getc(*stream++) would diagnose, provided that the line is executed. See sfx.h and sfx.c here: http://git.savannah.nongnu.org/cgit/kazlib.git/tree/ http://git.savannah.nongnu.org/cgit/kazlib.git/tree/
- kazinator 10y agoThere are all kinds of macros that have to evaluate an expression exactly once, and cannot be made into functions. ;; cond evaluated exactly once; ;; then or else at most once, not before cond. (if cond then else) getc doesn't have to be a macro. It illustrates just the point that the macro issues in C are so unfixable that broken macros have even been codified in ISO C.
- 10y ago