Reputation: 97
I am new to Lisp and I am reading through Doug Hoyte's Let Over Lambda and he presents Paul Graham's nif
macro in Chapter 3. I was playing around with that and made these two macros:
(defmacro niffy (expr pos zero neg)
`(cond ((plusp ,expr) ,pos)
((zerop ,expr) ,zero)
(t ,neg)))
(defmacro niffy2 (expr pos zero neg)
`(let ((x ,expr))
(cond ((plusp x) ,pos)
((zerop x) ,zero
(t ,neg)))))
When I do (macroexpand '(niffy2 10 "positive" "zero" "negative"))
, I get what I expect: (LET ((X 10)) (COND ((PLUSP X) "positive") ((ZEROP X) "zero" (T "negative"))))
But when I do (macroexpand '(niffy 10 "positive" "zero" "negative"))
I just get the evaluated form "positive"
. Which confuses me because in niffy
, cond
is backquoted, so I thought that meant it wouldn't be evaluated. Evaluating both niffy
and niffy2
without the macro expansion both work exactly as I expect, returning "positive", "zero", and "negative" for positive, zero, and negative values respectively.
Upvotes: 3
Views: 132
Reputation: 139251
Your code has an error:
(defmacro niffy2 (expr pos zero neg)
`(let ((x ,expr))
(cond ((plusp x) ,pos)
((zerop x) ,zero <- missing parenthesis
(t ,neg))))) <- T is not a function
Test:
(defun test (x)
(niffy2 x "positive" "zero" "negative"))
The compiler complains:
The following function is undefined:
T which is referenced by TEST
It should be:
(defmacro niffy2 (expr pos zero neg)
`(let ((x ,expr))
(cond ((plusp x) ,pos)
((zerop x) ,zero)
(t ,neg))))
But when I do (macroexpand '(niffy 10 "positive" "zero" "negative")) I just get the evaluated form "positive".
Since you call for a macro expansion, it can't be evaluated. It must be an effect of the macro expander in that implementation.
Note that MACROEXPAND
is an iterative process. The form will be expanded. The result form then could be another macro form. It will be expanded, too. And again. Again. Until the result form is not a macro form. Note that this does not traverse the subforms. It's strictly doing iterative macro expansion of the top form.
Use MACROEXPAND-1
So if you want to see the effects of only your macro, call MACROEXPAND-1
. This function will expand exactly once.
CL-USER 23 > (macroexpand-1 '(niffy 10 "positive" "zero" "negative"))
(COND ((PLUSP 10) "positive")
((ZEROP 10) "zero")
(T "negative"))
CL-USER 24 > (macroexpand-1 '(COND ((PLUSP 10) "positive")
((ZEROP 10) "zero")
(T "negative")))
(IF (PLUSP 10)
(PROGN "positive")
(IF (ZEROP 10)
(PROGN "zero")
(PROGN "negative")))
T
Upvotes: 1
Reputation: 213298
This is because cond
is a macro. Try running the following:
(macroexpand
'(cond ((plusp 10) "positive")
((zerop 10) "negative")
(t "zero")))
In SBCL, I get this:
(IF (PLUSP 10)
(PROGN "positive")
(COND ((ZEROP 10) "negative") (T "zero")))
T
In CLISP, I get this:
"positive" ;
T
While I can understand CLISP's behavior, it seems a bit needless, since the optimization could more easily be handled after macroexpansion.
(Note that you should ordinarily prefer the version with let
because it doesn't evaluate its argument multiple times.)
Upvotes: 3