Alex Rivera | Logout

What makes Lisp macros so special?

Asked 2008-11-06T07:33:11.977
365

Reading Paul Graham's essays on programming languages one would think that Lisp macros are the only way to go. As a busy developer, working on other platforms, I have not had the privilege of using Lisp macros. As someone who wants to understand the buzz, please explain what makes this feature so powerful.

Please also relate this to something I would understand from the worlds of Python, Java, C# or C development.

Edit
Report

4 Answers

116

You will find a comprehensive debate around lisp macro here.

An interesting subset of that article:

In most programming languages, syntax is complex. Macros have to take apart program syntax, analyze it, and reassemble it. They do not have access to the program's parser, so they have to depend on heuristics and best-guesses. Sometimes their cut-rate analysis is wrong, and then they break.

But Lisp is different. Lisp macros do have access to the parser, and it is a really simple parser. A Lisp macro is not handed a string, but a preparsed piece of source code in the form of a list, because the source of a Lisp program is not a string; it is a list. And Lisp programs are really good at taking apart lists and putting them back together. They do this reliably, every day.

Here is an extended example. Lisp has a macro, called "setf", that performs assignment. The simplest form of setf is

  (setf x whatever)

which sets the value of the symbol "x" to the value of the expression "whatever".

Lisp also has lists; you can use the "car" and "cdr" functions to get the first element of a list or the rest of the list, respectively.

Now what if you want to replace the first element of a list with a new value? There is a standard function for doing that, and incredibly, its name is even worse than "car". It is "rplaca". But you do not have to remember "rplaca", because you can write

  (setf (car somelist) whatever)

to set the car of somelist.

What is really happening here is that "setf" is a macro. At compile time, it examines its arguments, and it sees that the first one has the form (car SOMETHING). It says to itself "Oh, the prog

answered 2008-11-06T07:45:13.620
49

Lisp macros allow you to decide when (if at all) any part or expression will be evaluated. To put a simple example, think of C's:

expr1 && expr2 && expr3 ...

What this says is: Evaluate expr1, and, should it be true, evaluate expr2, etc.

Now try to make this && into a function... thats right, you can't. Calling something like:

and(expr1, expr2, expr3)

Will evaluate all three exprs before yielding an answer regardless of whether expr1 was false!

With lisp macros you can code something like:

(defmacro && (expr1 &rest exprs)
    `(if ,expr1                     ;` Warning: I have not tested
         (&& ,@exprs)               ;   this and might be wrong!
         nil))

now you have an &&, which you can call just like a function and it won't evaluate any forms you pass to it unless they are all true.

To see how this is useful, contrast:

(&& (very-cheap-operation)
    (very-expensive-operation)
    (operation-with-serious-side-effects))

and:

and(very_cheap_operation(),
    very_expensive_operation(),
    operation_with_serious_side_effects());

Other things you can do with macros are creating new keywords and/or mini-languages (check out the (loop ...) macro for an example), integrating other languages into lisp, for example, you could write a macro that lets you say something like:

(setvar *rows* (sql select count(*)
                      from some-table
                     where column1 = "Yes"
                       and column2 like "some%string%")

And thats not even getting into answered 2008-11-06T09:21:51.790

9

Lisp macros represents a pattern that occurs in almost any sizeable programming project. Eventually in a large program you have a certain section of code where you realize it would be simpler and less error prone for you to write a program that outputs source code as text which you can then just paste in.

In Python objects have two methods __repr__ and __str__. __str__ is simply the human readable representation. __repr__ returns a representation that is valid Python code, which is to say, something that can be entered into the interpreter as valid Python. This way you can create little snippets of Python that generate valid code that can be pasted into your actually source.

In Lisp this whole process has been formalized by the macro system. Sure it enables you to create extensions to the syntax and do all sorts of fancy things, but it's actual usefulness is summed up by the above. Of course it helps that the Lisp macro system allows you to manipulate these "snippets" with the full power of the entire language.

answered 2008-11-06T14:31:30.603
5

In short, macros are transformations of code. They allow to introduce many new syntax constructs. E.g., consider LINQ in C#. In lisp, there are similar language extensions that are implemented by macros (e.g., built-in loop construct, iterate). Macros significantly decrease code duplication. Macros allow embedding «little languages» (e.g., where in c#/java one would use xml to configure, in lisp the same thing can be achieved with macros). Macros may hide difficulties of using libraries usage.

E.g., in lisp you can write

(iter (for (id name) in-clsql-query "select id, name from users" on-database *users-database*)
      (format t "User with ID of ~A has name ~A.~%" id name))

and this hides all the database stuff (transactions, proper connection closing, fetching data, etc.) whereas in C# this requires creating SqlConnections, SqlCommands, adding SqlParameters to SqlCommands, looping on SqlDataReaders, properly closing them.

answered 2008-11-06T12:27:01.027

Your Answer