GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | ||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | ||||||||
GeezerVison is showing cl-cookbook.sourceforge.net — The Common Lisp Cookbook - Macros and Backquote The Common Lisp Cookbook - Macros and BackquoteContentsHow Macros WorkThe word macro is used generally in computer science to mean a syntactic extension to a programming language. (Note: The name comes from the word "macro-instruction," which was a useful feature of many second-generation assembly languages. A macro-instruction looked like a single instruction, but expanded into a sequence of actual instructions. The basic idea has since been used many times, notably in the C preprocessor. The name "macro" is perhaps not ideal, since it connotes nothing relevant to what it names, but we're stuck with it.) Although many languages have a macro facility, none of them are as powerful as Lisp's. The basic mechanism of Lisp macros is simple, but has subtle complexities, so learning your way around it takes a bit of practice.A macro is an ordinary piece of Lisp code that operates on
another piece of putative Lisp code, translating it into (a
version closer to) executable Lisp. That may sound a bit complicated,
so let's give a simple example. Suppose you want a version of (setq2 x y (+ z 3)) when It should be obvious that we can't define Here's how we could define the (defmacro setq2 (v1 v2 e) (list 'progn (list 'setq v1 e) (list 'setq v2 e)))This is very close to the following function definition: (defun setq2F (v1 v2 e) (list 'progn (list 'setq v1 e) (list 'setq v2 e)))If we evaluated (setq2F 'x 'y '(+ z
3)), we would get (progn (setq x (+ z 3)) (setq y (+ z
3))). This is a perfectly ordinary Lisp computation, whose
sole point of interest is that its output is a piece of executable
Lisp code. What defmacro does is create this function
implicitly and arrange that whenever an expression of the form
(setq2 x y (+ z 3)) is seen, setq2F is
called with the pieces of the form as arguments, namely
x, y, and (+ z 3). The
resulting piece of code then replaces the call to setq2,
and execution resumes as if the new piece of code had occurred in the
first place. The macro form is said to expand into the new
piece of code.
This is all there is to it, except, of course, for the myriad
subtle consequences. The main consequence is that run time for
the Novices often make the following sort of mistake. Suppose that the
(defmacro setq2 (v1 v2 e)
(let ((e1 (comtran e)))
(list 'progn (list 'setq v1 e1) (list 'setq v2 e1))))
The mistake is to suppose that once a macro is called, the Lisp
system enters a "macro world," so naturally everything in
that world must be defined using (defmacro setq2 (v1 v2 e)
(let ((e1 (comtran e)))
(list 'progn (list 'setq v1 e1) (list 'setq v2 e1))))
One possible explanation for this mistake may be that in other languages, such as C, invoking a preprocessor macro does get you into a different world; you can't run an arbitrary C program. It might be worth pausing to think about what it might mean to be able to. Another subtle consequence is that we must spell out how the arguments
to the macro get distributed to the hypothetical behind-the-scenes
function (called (defmacro foo (x &optional y &key (cxt 'null)) ...)then
Note that the values of the variables are the actual expressions (defmacro setq-reversible (e1 e2 d)
(case d
(:normal (list 'setq e1 e2))
(:backward (list 'setq e2 e1))
(t (error ...))))
BackquoteBefore taking another step, we need to introduce a piece of Lisp notation that is indispensable to defining macros, even though technically it is quite independent of macros. This is the backquote facility. As we saw above, the main job of a macro, when all is said and done, is to define a piece of Lisp code, and that means evaluating expressions such as(list
'prog (list 'setq ...) ...). As these expressions grow in
complexity, it becomes hard to read them and write them. What we find
ourselves wanting is a notation that provides the skeleton of an
expression, with some of the pieces filled in with new expressions.
That's what backquote provides. Instead of the the list
expression given above, one writes
`(progn (setq ,v1 ,e) (setg ,v2 ,e))The backquote ( `) character signals that in the expression that
follows, every subexpression not preceded by a comma is to be
quoted, and every subexpression preceded by a comma is to be
evaluated.
That's mostly all there is to backquote. There are just two extra
items to point out. First, if you write " Second, one might wonder what happens if a backquote expression occurs inside another backquote. The answer is that the backquote becomes essentially unreadable and unwriteable; using nested backquote is usually a tedious debugging exercise. The reason, in my not-so-humble opinion, is that backquote is defined wrong. A comma pairs up with the innermost backquote when the default should be that it pairs up with the outermost. But this is not the place for a rant or tutorial; consult your favorite Lisp reference for the exact behavior of nested backquote plus some examples. One problem with backquote is that once you learn it you tend to use for every list-building occasion. For instance, you might write (mapcan (lambda (x)
(cond ((symbolp x) `((,x)))
((> x 10) `(,x ,x))
(t '())))
some-list)
which yields ((a) 15 15) when some-list = (a 6 15).
The problem is that mapcan destructively alters the results
returned by the lambda-expression. Can we be sure that the
lists returned by that expression are "fresh," that is, they are
different (in the eq sense) from the structures returned on
other calls of that lambda expression? In the present case,
close analysis will show that they must be fresh, but in general
backquote is not obligated to return a fresh list every time (whether
it does or not is implementation-dependent). If the example above got
changed to
(mapcan (lambda (x)
(cond ((symbolp x) `((,x)))
((> x 10) `(,x ,x))
((>= x 0) `(low))
(t '())))
some-list)
then backquote may well treat `(low) as if it were '(low);
the list will be allocated at load time, and every time the lambda is evaluated, that same chunk of storage will be returned. So
if we evaluate the expression with some-list = (a 6 15),
we will get ((a) low 15 15), but as a side effect the constant
(low) will get clobbered to become (low 15 15). If we
then evaluate the expression with, say, some-list = (8
oops), the result will be (low 15 15 (oops)), and now the
"constant" that started off as '(low) will be (low 15 15
(oops)). (Note: The bug exemplified here takes other forms, and has
often bit newbies - as well as experienced programmers - in the
ass. The general form is that a constant list is produced as the
value of something that is later destructively altered. The first
line of defense against this bug is never to destructively alter any
list. For newbies, this is also the last line of defense. For those
of us who imagine we're more sophisticated, the next line of defense is
to think very carefully any time you use nconc or mapcan.)
To fix the bug, you can write (mapcan (lambda (x)
(cond ((symbolp x) (list `(,x)))
((> x 10) (list x x))
((>= x 0) (list 'low))
(t '())))
some-list)
My personal preference is to use backquote only to build S-expressions, that is, hierarchical expressions that consist of symbols, numbers, and strings, and that are not conceptualized as changing in length. For instance, I would never write (setq sk `(,x ,@sk))If sk is being used as a stack, that is, it's going to be popped in the normal course of things, I would write tt (push x
sk). If not, I would write (setq sk (cons x sk)).
Getting Macros RightI said in the first section that my definition ofsetq2 wasn't quite right, and now it's time to fix it.
Suppose we write The solution is to evaluate (defmacro setq2 (v1 v2 e)
(let ((tempvar (gensym)))
`(let ((,tempvar ,e))
(progn (setq ,v1 ,tempvar)
(setq ,v2 ,tempvar)))))
Now (setq2 x y (+ x 3)) expands to
(let ((#:g2003 (+ x 3)))
(progn (setq x #:g2003) (setq y #:g2003)))
Here gensym has returned the symbol #:g2003, which prints
in this funny way because it won't be recognized by the reader. (Nor
is there any need for the reader to recognize it, since it exists only
long enough for the code that contains it to be compiled.)
Exercise: Verify that this new version works correctly for the case
Exercise: Try writing the new version of the macro without using backquote. If you can't do it, you have done the exercise correctly, and learned what backquote is for! The moral of this section is to think carefully about which
expressions in a macro get evaluated and when. Be on the lookout for
situations where the same expression gets plugged into the output
twice (as What Macros are ForMacros are for making syntactic extensions to Lisp. One often hears it said that macros are a bad idea, that users can't be trusted with them, and so forth. Balderdash. It is just as reasonable to extend a language syntactically as to extend it by defining your own procedures. It may be true that the casual reader of your code can't understand the code without seeing the macro definitions, but then the casual reader can't understand it without seeing function definitions either. Havingdefmethods strewn around several files
contributes far more to unclarity than macros ever have, but that's a
different diatribe.
Before surveying what sorts of syntactic extensions I have found useful, let me point out what sorts of syntactic extensions are generally not useful, or best accomplished using means other than macros. Some novices think macros are useful for open-coding functions. So, instead of defining (defun sqone (x) (let ((y (+ x 1))) (* y y)))they might define (defmacro sqone (x) `(let ((y (+ ,x 1))) (* y y)))So that (sqone (* z 13)) might expand into
(let ((y (+ (* z 13) 1))) (* y y))This is correct, but a waste of effort. For one thing, the amount of time saved is almost certainly negligible. If it's really important that sqone be expanded inline, one can put (declaim (inline
sqone)) before sqone is defined (although the compiler is not
obligated to honor this declaration). For another, once sqone
is defined as a macro, it becomes impossible to write (mapcar
#'sqone ll), or
to do anything else with it except call it.
But macros have a thousand and one legitimate uses. Why write Many people find (mapcar (lambda (x)
(let ((y (hairy-fun1 x)) (z (hairy-fun2 x)))
(dolist (y1 y)
(dolist (z1 z)
... and further meaningless
space-filling nonsense...
))))
l)
we might prefer to write
(for (x :in l)
(let ((y (hairy-fun1 x)) (z (hairy-fun2 x)))
(dolist (y1 y)
(dolist (z1 z)
... and further meaningless
space-filling nonsense...
))))
This macro might be defined thus: (defmacro for (listspec exp)
(cond ((and (= (length listspec) 3)
(symbolp (car listspec))
(eq (cadr listspec) ':in))
`(mapcar (lambda (,(car listspec))
,exp)
,(caddr listspec)))
(t (error "Ill-formed: %s" `(for ,listspec ,exp)))))
(This is a simplified version of a macro by Chris Riesbeck.)
It's worth stopping for a second to discuss the role
the keyword It is not strictly necessary for the guide symbols of a macro to be in
the keyword package, but it is a good idea, for two reasons. First,
they highlight to the reader that something idiosyncratic is going
on. A form like Second, notice that I wrote (and (symbolp (cadr listspec))
(eq (intern (symbol-name (cadr listspec))
:keyword)
':in))
Another would be to write
(and (symbolp (cadr listspec))
(string= (symbol-name (cadr listspec)) (symbol-name 'in)))
which
neither of which is particularly clear or aesthetic.
The keyword package is there to provide a home for
symbols whose home is not per se relevant to anything; you might as
well use it. (Note: In ANSI Lisp,
I could have written "IN" instead of (symbol-name 'in), but there are Lisp implementations that do not
convert symbols' names to uppercase. Since I think the whole
uppercase conversion idea is an embarrassing relic, I try to write
code that is portable to those implementations.)
Let's look at another example, both to illustrate a nice macro, and to
provide an auxiliary function for some of the discussion below. One
often wants to create new symbols in Lisp, and For example, (build-symbol (:< x) "-" (:++ *x-num*)), when x = foo and
*x-num* = 8, sets *x-num* to 9 and
evaluates to
FOO-9. If evaluated again, the result will be FOO-10, and so
forth.
Obviously, (defmacro build-symbol (&rest l)
(let ((p (find-if (lambda (x) (and (consp x) (eq (car x) ':package)))
l)))
(cond (p
(setq l (remove p l))))
(let ((pkg (cond ((eq (cadr p) 'nil)
nil)
(t `(find-package ',(cadr p))))))
(cond (p
(cond (pkg
`(values (intern ,(symstuff l) ,pkg)))
(t
`(make-symbol ,(symstuff l)))))
(t
`(values (intern ,(symstuff l))))))))
(defun symstuff (l)
`(concatenate 'string
,@(for (x :in l)
(cond ((stringp x)
`',x)
((atom x)
`',(format nil "~a" x))
((eq (car x) ':<)
`(format nil "~a" ,(cadr x)))
((eq (car x) ':++)
`(format nil "~a" (incf ,(cadr x))))
(t
`(format nil "~a" ,x))))))
(Another approach would be have Sometimes a macro is needed only temporarily, as a sort of syntactic scaffolding. Suppose you need to define 12 functions, but they fall into 3 stereotyped groups of 4: (defun make-a-zip (y z) (vector 2 'zip y z)) (defun test-whether-zip (x) (and (vectorp x) (eq (aref x 1) 'zip))) (defun zip-copy (x) ...) (defun zip-deactivate (x) ...)Where the omitted pieces are the same in all similarly named functions. (That is, the "..." in zep-deactivate is the
same code as the "..." in zip-deactivate, and so forth.)
Here, for the sake of concreteness, if not plausibility, zip,
zap, and zep are behaving like odd little data structures.
The functions could be rather large, and it would get tedious
keeping them all in synch as they are debugged. An alternative would
be to use a macro:
(defmacro odd-define (name buildargs)
`(progn (defun ,(build-symbol make-a- (:< name))
,buildargs
(vector ,(length buildargs) ',name ,@buildargs))
(defun ,(build-symbol test-whether- (:< name)) (x)
(and (vectorp x) (eq (aref x 1) ',name))
(defun ,(build-symbol (:< name) -copy) (x)
...)
(defun ,(build-symbol (:< name) -deactivate) (x)
...))))
If all the uses of this macro are collected in this one place, it
might be clearer to make it a local macro using (macrolet ((odd-define (name buildargs)
`(progn (defun ,(build-symbol make-a- (:< name))
,buildargs
(vector ,(length buildargs)
',name
,@buildargs))
(defun ,(build-symbol test-whether- (:< name))
(x)
(and (vectorp x) (eq (aref x 1) ',name))
(defun ,(build-symbol (:< name) -copy) (x)
...)
(defun ,(build-symbol (:< name) -deactivate) (x)
...)))))
(odd-define zip (y z))
(odd-define zap (u v w))
(odd-define zep ()))
Finally, macros are essential for defining "command languages." A command is a function with a short name for use by users in interacting with Lisp's read-eval-print loop. A short name is useful and possible because we want it to be easy to type and we don't care much whether the name clashes some other command; if two command names clash, we can change one of them. As an example, let's define a little command language for debugging
macros. (You may actually find this useful.) There are just two
commands,
Suppose you're trying to debug a macro > (ex (hair-squared ...))
(PROGN (DEFUN ...)
(ODD-DEFINE ZIP (U V W))
...)
> (fi odd-define)
(ODD-DEFINE ZIP (U V W))
> (ex)
(PROGN (DEFUN MAKE-A-ZIP (U V W) ...)
...)
Once again, it is clear that Here is how (defvar *current-form*)
(defmacro ex (&optional (form nil form-supplied))
`(progn
(pprint (setq *current-form*
(macroexpand-1
,(cond (form-supplied
`',form)
(t '*current-form*)))))
(values)))
(defmacro fi (s &optional (k 0))
`(progn
(pprint (setq *current-form*
(find-nth-occurrence ',s *current-form* ,k)))
(values)))
The In some Lisp
implementations, read-eval-print loops routinely print results using
I leave the definition of One caution: In general, command languages
will consist of a mixture of macros and functions, with convenience
for their definer (and usually sole user) being the main
consideration. If a command seems to "want" to evaluate some of its
arguments sometimes, you have to decide whether to define two (or
more) versions
of it, or just one, a function whose arguments must be quoted to
prevent their being evaluated. For the Copyright © 2002-2007 The Common Lisp Cookbook Project http://cl-cookbook.sourceforge.net/ $Header: /cvsroot/cl-cookbook/cl-cookbook/macros.html,v 1.9 2007/01/16 20:58:32 ozten Exp $ | ||||||||
| You are visitor 01402503 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |