Search code examples
c++macroslisp

Lisp macro special usage?


I read Hackers and Painters book recently. The author said the Lisp macro is very powerful and special than other language's macro.

I began to learn macro of ELisp,but still don't see what special usage of it? Can't see the difference between it and C/C++ macro.

Could anybody explain it for me?


Solution

  • I think the most important points are:

    • Lisp code is represented as lists, and with Lisp macros, you can use all of Lisp's list processing functionality (which is something Lisp is well suited for, remember that LISP stands for LISt Processing) to transform it. (Homoiconicity) C/C++ preprocessor macros, however, are mere textual substitutions. That is, to perform more complex transformations, you would have to parse the input to a C/C++ preprocessor macro yourself (and you usually don't have access to the parser in those languages). You can't use all of C/C++ or whatever the language at hand to transform your code. These simple macro facilities seem to be more suited for little abbreviations (or bypassing the type system, performance workarounds, etc.), than for real abstraction. (Not that one couldn't use them for more complex tasks, but it quickly becomes a mess.)

    • Lisp syntax is also much more regular than the usual curly brace languages. You don't have to wory about precedence rules, infix operators, or the distinction between statements and expressions.

    • In contrast to Lisp macros, as far as I know, with C/C++ preprocessor macros, you can't easily use macros within macro definitions without jumping through hoops, because the preprocessor makes only one pass.

    • There is no easy way (that I know about) to introduce fresh symbols in C/C++ style preprocessor macros. (You can use gensym and abstractions built around it like with-gensyms etc. in CL style macros, and there also are hygienic macro systems, mostly used in the Scheme dialects.)

    • Backquoted expressions in Lisp are a really convenient fill-in-the-blanks approach to build the code a macro expands to.

    All in all, metaprogramming with Lisp macros is far more convenient than approaches with the C/C++ preprocessor. In practice, this means, that even a relative newcomer to Lisp would be able to whip up his own looping constructs or other control structures without much hassle. With a little more experience, writing more complex DSLs also becomes relatively easy. Compare this to the C/C++ preprocessor, where these tasks would probably be considered to be some kind of black magic, reserved for only the bravest.

    As a quick example, try writing something like this with the C/C++ preprocessor:

    (defmacro bif ((var expression) then &optional else)
      `(let ((,var ,expression))
         (if ,var ,then ,else)))
    

    It introduces a new control structure bif ("binding if") which evaluates an expression, binds it to a given symbol, and then conditionally executes the then or else branch with the binding in scope. It nests properly and works as expected. Now, things like this can be written by any Lisp programmer worth their salt in no time. Since it's so easy, Lisp programmers usually don't hesitate when they feel the need for a new construct – the barrier between language designer and language user is blurred.

    (Even if one manages to write this using the C/C++ preprocessor, the next step, i.e. writing something like Lisp's cond, or a new looping construct, would get much more complex quickly.)