One of the key components in extending LISP or building new 
languages on top of LISP is the "macro".  A macro works like 
a function, except that when LISP sees a call to a macro in 
some code that is being executed, the definition of the macro 
is inserted in place of the macro call.  This is in contrast 
to a "normal" function call which results in a copy of the 
current environment being saved on the program stack, 
followed by the execution of the function definition, which 
in turn is followed by the "popping" of the program stack.  
Another way to think of the difference between functions and 
macros is this:  A function produces results, while a macro 
produces expressions which, when evaluated, produce results.

We've seen lots of macros in use already: dolist, dotimes, cond,
setf, do, defun, case, etc.  You should look at pp. 1026-27 
of Steele for an exhaustive list of predefined LISP macros.

Macros and their arguments

One big important feature of macros is that they allow you to 
create constructs which do not evaluate their arguments.  The 
importance of this may not be immediately obvious.  An 
example will help you see the light.  Say that you had a 
version of LISP in which there was no "if" form, and you 
wanted to create one.  How would you do it?

Your first thought might be to create an ordinary function:

   (defun my-if (test then-part else-part)
     (cond (test then-part)
           (T else-part)))

That looks reasonable, no?  Let's test it:

   ? (my-if t 2 3)
   2
   ? (my-if nil 2 3)
   3

Perfect.  Exactly what we want.  Let's test it just a little 
bit more:

   ? (my-if t (print "yes") (print "no"))
   
   "yes" 
   "no" 
   "yes"

OOPS!!  What happened here?  Why does "my-if" do that?  The 
problem is that "my-if" evaluates its arguments, and these 
particular arguments have side-effects when evaluated.  So, 
even before the "my-if" definition is applied to the 
arguments, the two "print" expressions are evaluated, 
resulting in both "yes" and "no" being printed on the 
terminal.  Only then is the function applied to the 
arguments, and the result of evaluating (print "yes") is 
returned.  But that's not what "if" does, is it?

How then does "if" work?  It's actually something called a 
special form, hard-coded into the LISP compiler.  You 
can't write a special form; they're written only by the folks 
who created the compiler.  But, "if" could be implemented 
as a macro, and that's something you could do yourself.  How?  
By using another macro called "defmacro":

   (defmacro my-if (test then-part else-part)
     (list 'cond (list test then-part)
                 (list 'T else-part)))

When LISP then sees:

   (my-if nil (print "yes") (print "no"))

it first "expands the macro", replacing the macro call with:

   (cond (nil (print "yes"))
         (T   (print "no")))

In other words, anything that's quoted is used literally, and 
anything that's not quoted is replaced by the value it's 
bound to at the time the macro call is encountered.


Backquote-comma syntax

We could write our macros as shown above, but the macros that 
result don't look much like the code they're going to expand 
into.  This makes macros hard to read and debug.  So a syntax 
for macro creation has been invented, which when used makes 
these things look more like templates for the code to be 
generated.

The backquote-comma syntax introduces two new operators: the 
backquote (`) and the comma (,).  What did you expect?  The 
backquote tells LISP to quote everything in the list that 
follows (so in that sense it works just like quote), except 
for those items immediately preceded by a comma.

The comma works only within the scope of a backquote.  It 
says "turn off the quoting for now".  When it appears in 
front of a list element, it cancels out the quote that would 
have been in effect otherwise.  So instead of the literal 
symbol being put into that template, the value of that symbol 
at the time of the macroexpansion is put there.

In other words, backquote makes a template while comma makes 
a slot in the template.

Here's "my-if" again, using backquote-comma syntax:

   (defmacro my-if (test then-part else-part)
     `(cond (,test ,then-part)
            (T ,else-part)))

Note: In the LISP version of "if", the "else-part" is 
actually an optional argument.  We could get that same effect 
here by doing the following:

   (defmacro my-if (test then-part &optional else-part)
               :
               :

In either case, this expands exactly the same way as our 
previous macro definition, but it's much easier to read 
because it looks much more like the code that it will turn 
into, no?


How to define simple macros (taken from "On LISP" by Paul Graham)

Let's walk through another macro example in gory detail and, along 
the way, we'll outline a step-by-step process for defining macros.

You're familiar with "member", right?  This function uses "eql" to 
test for equality by default.  But you could use an optional 
argument to use a different test:

   (member item my-list :test #'eq)

If we use this a lot, we could make our own variant of "member" 
that always uses "eq".  In older dialects of LISP, this was called 
"memq":

   (memq item my-list)

We could define "memq" as an ordinary function:

   (defun memq (item my-list)
     (member item my-list :test #'eq))

but that wouldn't tell us much about macros, would it?  Let's 
define "memq" as a macro.  Graham tells us that the first step in 
macro definition is to begin with a typical call to the macro that 
we want to define, and write it on a piece of paper.  Below that, 
we should write the expression that we want the macro call to 
expand into:

call:        (memq item my-list)


expansion:   (member item my-list :test #'eq)

Then, we begin the macro definition like this:

(defmacro memq

And we construct the argument list for the macro definition from 
the parameters in the macro call.  To avoid confusion, we invent 
new names for each of these arguments:

(defmacro memq (obj lst)

Now we go back to the call and expansion that we wrote down.  For 
each argument in the macro call, we draw a line connecting it with 
the place it appears in the expansion:

call:        (memq item my-list)
                     \     \
                      \     \
expansion:   (member item my-list :test #'eq)

Now we look at the expansion, and while reading the expansion, we 
start constructing the macro body.  We begin with a backquote:

(defmacro memq (obj lst)
  `

Then, whenever we see a parenthesis in the expansion that isn't 
part of an argument in the macro call, we put that same 
parenthesis in the macro definition:

(defmacro memq (obj lst)
  `(

And then, for each expression in the expansion, we do the 
following:

1.  If there is no line connecting it with the macro call, 
    we write down the expression itself, as is.

2.  If there is a connection to one of the arguments in the
    macro call, we write down the symbol which occurs in the
    corresponding position in the macro parameter list, 
    immediately preceded by a comma (i.e., we don't want this
    quoted...instead, we want to pass a value through here).

Since there's no connecting line to the first element, "member", 
we write it down as is:

(defmacro memq (obj lst)
  `(member

On the other hand, "item" does have a connecting line, so in the 
macro body we insert the corresponding parameter, preceded by a 
comma:

(defmacro memq (obj lst)
  `(member ,obj

And we do this stuff until we're done:

(defmacro memq (obj lst)
  `(member ,obj ,lst :test #'eq))

That was easy, wasn't it?

The LISP evaluator, revisited

All this macro stuff suggests that our concept of what the LISP 
evaluator does isn't quite complete.  Let's update that---here's 
how it really works (taken from "Common LISP Programming for 
Artificial Intelligence" by Hasemer and Domingue):

1.  If the expression is an atom, return its value.
2.  If the expression is a list, then either
    a.  If the first member of the list is a special form,
        handle it and its arguments accordingly.
    b.  If the first member of the list is the name of a 
        macro, call the function "macroexpand" to expand it.
        Then go to step 1.
    c.  If the first member of the list is the name of a 
        function, then:
        i.   Retrieve the function body
        ii.  Find the values of the remaining members of the
             list by applying these rules to each of them 
             in turn.
        iii. Apply the function from (i) to the arguments
             from (ii) and return the result.

Things to remember:  First, as noted before, macro expansion does 
not evaluate the macro's arguments.  The macro is expanded before 
the arguments are evaluated by the evaluator.  Second, macro 
expansion is performed as long as the first element of the 
expression is the name of a macro.

The "macroexpand" function

The key to macro expansion in LISP is the "macroexpand" function.  
This function takes one argument, which is quoted.  And that 
argument is a macro call.  So let's say we define our "my-if" 
macro:


   ? (defmacro my-if (test then-part else-part)
       `(cond (,test ,then-part)
              (T ,else-part)))
   MY-IF


If we then want to see how a macro call to "my-if" will expand, we 
can do this:


   ? (macroexpand '(my-if (eql 1 2) (print "yes") (print "no")))
   (IF (EQL 1 2) (PROGN (PRINT "yes")) (COND (T (PRINT "no"))))
   T


Note that "macroexpand" returns not one result, but two.  Weird, 
eh?  Read  pp. 179-187 of Steele for more about this.  In any case, 
the first value is the expanded code, and the second result tells 
us whether the first element of the argument was the name of a 
macro.  If it was, then the second value returned will be T, and 
the first value will be different from the input argument.  If the 
first element was not the name of a macro, the second value 
returned will be nil, and the first value will be the unchanged 
input argument.

Also note that the result of the expansion above isn't what we 
expected, at least based on our macro definition.  That's because 
we put a macro call inside our macro definition---"cond" is the 
name of a macro.  So "macroexpand" did one expansion of the call 
to "my-if", and the result of that was an expression beginning 
with "cond".  Since "cond" is the name of a macro, "macroexpand" 
was called again, resulting in yet another expansion.  This new 
expression began with "if", which is a special form, not a macro, 
so macroexpansion ceased at that point.

If we want to see the result of just one level of expansion so we 
can check to see if our macro expands the way we intended it to, 
we use another function called "macroexpand-1".  Here's the result 
of one level of expansion:


   ? (macroexpand-1 '(my-if (eql 1 2) (print "yes") (print "no")))
   (COND ((EQL 1 2) (PRINT "yes")) (T (PRINT "no")))
   T


Here's the what happens when we expand the result of the previous 
expansion:


   ? (macroexpand-1 '(COND ((EQL 1 2) (PRINT "yes")) (T (PRINT 
   "no"))))
   (IF (EQL 1 2) (PROGN (PRINT "yes")) (COND (T (PRINT "no"))))
   T


And here's what happens when we try to expand that result, which 
starts with the name of a special form, not a macro:


   ? (macroexpand-1 '(IF (EQL 1 2) (PROGN (PRINT "yes")) (COND (T 
   (PRINT "no")))))
   (IF (EQL 1 2) (PROGN (PRINT "yes")) (COND (T (PRINT "no"))))
   NIL


Advantages and disadvantages of macros

There are two key advantages to using macros:

1.  The arguments to macros are not evaluated.  Thus, new
    syntactic constructs can be defined with macros.  This
    is essential to defining new languages on top of LISP,
    especially languages that aren't constrained in ways 
    that functional programming languages often are.

2.  Macros produce faster (but more) code than functions
    because they avoid the overhead of function calls.

But there are also disadvantages:

1.  If you compile macros, you can't detect them with your
    usual debugging tools, so "trace" and "step" won't work.

2.  Since macros aren't functions, they can't be used with
    "funcall", "apply", or the mapping functions.

3.  Again, if you compile macros, when you redefine a macro,
    you must recompile every function whose sourdce contains 
    a call to the macro.  If you don't, your source code may
    contain the results of expanding old macro definitions 
    instead of the new ones.  You may think the new macro has
    been expanded where the macro call occurs, but it won't
    be.  This can be especially aggravating if the old macro
    definition resulted in code which didn't blow up.  So 
    your program does something, it just doesn't do what your
    new macro definition says it should do.  You can spend
    lots of hours spinning wheels trying to debug that kind
    of thing.

4.  Macros produce more (but faster) code than functions
    because additional code is added to your program every
    time a macro call is expanded.


Lecture notes by Kurt Eiselt, 1998.
Minor changes / additions by Brian McNamara, 1998.
Last updated on Fri Aug 21 12:08:19 EDT 1998 by Brian McNamara