Chapter 3 - Essential Lisp in Twelve Lessons

Lesson 8 - Essential Macro Definition

DEFMACRO defines named macros

A DEFMACRO form looks a lot like a DEFUN form (see Lesson 7) -- it has a name, a list of argument names, and a body:

(defmacro name (argument ...)

Macros return a form, not values

The macro body returns a form to be evaluated. In other words, you need to write the body of the macro such that it returns a form, not a value. When Lisp evaluates a call to your macro, it first evaluates the body of your macro definition, then evaluates the result of the first evaluation. (By way of comparison, a function's body is evaluated to return a value.)

Here are a couple of simple macros to illustrate most of what you need to know:

? (defmacro setq-literal (place literal)
`(setq ,place ',literal))

? (setq-literal a b)
-> B

? a
-> B

? (defmacro reverse-cons (rest first)
`(cons ,first ,rest))

? (reverse-cons nil A)
-> (B)

SETQ-LITERAL works like SETQ, except that neither argument is evaluated. (Remember that SETQ evaluates its second argument.) The body of SETQ-LITERAL has a form that begins with a ` (pronounced "backquote"). Backquote behaves like quote -- suppressing evaluation of all the enclosed forms -- except where a comma appears within the backquoted form. A symbol following the comma is evaluated.

So in our call to (SETQ-LITERAL A B) above, here's what happens:

  1. bind PLACE to the symbol A.
  2. bind LITERAL to the symbol B.
  3. evaluate the body `(SETQ ,PLACE ',LITERAL), following these steps:
    1. evaluate PLACE to get the symbol A.
    2. evaluate LITERAL to get the symbol B.
    3. return the form (SETQ A 'B).
  4. evaluate the form (SETQ A 'B).

Neither the backquote nor the commas appear in the returned form. Neither A nor B is evaluated in a call to SETQ-LITERAL, but for different reasons. A is unevaluated because it appears as the first argument of SETQ. B is unevaluated because it appears after a quote in the form returned by the macro.

The operation of (REVERSE-CONS NIL A) is similar:

  1. bind REST
  2. to the symbol NIL.
  3. bind FIRST to the symbol A.
  4. evaluate the body `(CONS ,FIRST ,REST), following these steps:
    1. evaluate FIRST to get the symbol A.
    2. evaluate REST to get the symbol NIL.
    3. return the form (CONS A NIL).
  5. evaluate the form (CONS A NIL).

Both arguments of REVERSE-CONS are evaluated because CONS evaluates its arguments, and our macro body doesn't quote either argument. A evaluates to the symbol B, and NIL evaluates to itself.

If you want to see how your macro body appears before evaluation, you can use the MACROEXPAND function:

? (macroexpand '(setq-literal a b))
-> (SETQ A 'B)

? (macroexpand '(reverse-cons nil a))

Since MACROEXPAND is a function, it evaluates its arguments. This is why you have to quote the form you want expanded.

The examples in this lesson are deliberately very simple, so you can understand the basic mechanism. In general, macros are trickier to write than functions -- in Chapter 20 we'll look at the reasons and the correct techniques for dealing with more complex situations.

Contents | Cover
Chapter 2 | Chapter 3, Introduction | Chapter 3, Lesson 7 | Chapter 3, Lesson 8 | Chapter 3, Lesson 9 | Chapter 4 |

Copyright © 1995-2001, David B. Lamkins
All Rights Reserved Worldwide

This book may not be reproduced without the written consent of its author. Online distribution is restricted to the author's site.