Macros are expanded at compiler pre-processor time. Thus, the values of the arguments are generally not available, and code that tries to make use of them will not work. I.e. consider the following definition of
Square, which tries to replace
(Square 4) with
16 instead of with
(* 4 4).
(defmacro Square (X) (* X X))This would indeed work for
(Square 4), but would crash for
(Square X), since
X is probably a variable whose value is not known until run-time. Since macros do sometimes make use of variables and functions at expansion time, and to simplify debugging in general,
it is strongly recommended that all macro definitions and any variables and functions that they use at expansion time (as opposed to code they actually expand into) be placed in a separate file that is loaded before any files containing code that makes use of the macros.
(B) eva luating arguments too many times
Let's take another look at our first definition of the Square macro.
(defmacro Square (X) '(* ,X ,X))
This looks OK on first blush. However, try macroexpand-1'ing a form, and you notice that it eva luates its arguments twice:
(macroexpand-1 '(Square (Foo 2))) ==> (* (Foo 2) (Foo 2))
Foo gets called twice, but it should only be called once. Not only is this inefficient, but could return the wrong value if
Foo does not always return the same value. I.e. consider
Next-Integer, which returns 1 the first time called, then 2, then 3.
(Square (Next-Integer)) would return N*(N+1), not N
2, plus would advance N by 2. Similarly,
(Square (random 10)) would not necessarily generate a perfect square! With Lisp you have the full power of the language available at preprocessor time (unlike in C), so you can use ordinary Lisp constructs to solve this problem. In this case,
let can be used to store the result in a local variable to prevent multiple eva luation. There is no general solution to this type of problem in C.
(defmacro Square2 (X)
'(let ((Temp ,X))
(* Temp Temp)))
(macroexpand-1 '(Square2 (Foo 2)))
==> (let ((Temp (Foo 2)))
(* Temp Temp))
This is what we want.
(C) Variable name clashes.
When using
let to suppress multiple eva luation, one needs to be sure that there is no conflict between the local variable chosen and any existing variable names. The above version of
Square2 is perfectly safe, but consider instead the following macro, which takes two numbers and squares the sum of them:
(defmacro Square-Sum (X Y)
'(let* ((First ,X)
(Second ,Y)
(Sum (+ First Second)))
(* Sum Sum)) )
This looks pretty good, even after macroexpansion:
(macroexpand-1 '(Square-Sum 3 4))
==> (LET* ((FIRST 3)
(SECOND 4)
(SUM (+ FIRST SECOND)))
(* SUM SUM))
which gives the proper result. However, this version has a subtle problem. The local variables we chose would conflict with existing local variable names if a variable named
First already existed. E.g.
(macroexpand-1 '(Square-Sum 1 First))
==> (LET* ((FIRST 1)
(SECOND FIRST)
(SUM (+ FIRST SECOND)))
(* SUM SUM))
The problem here is that (SECOND FIRST) gets the value of the new local variable FIRST, not the one you passed in. Thus
(let ((First 9)) (Square-Sum 1 First))returns 4, not 100! Solutions to this type of problem are quite complicated, and involve using
gensym to generate a local variable name that is guaranteed to be unique.
Moral: even seemingly simple macros are hard to get right, so don't use macros unless they really add something. Both Square and Square-Sum are inappropriate uses of macros.
(defmacro Square