Previous: Eval During Expansion, Up: Problems with Macros [Contents][Index]
Occasionally problems result from the fact that a macro call is expanded each time it is evaluated in an interpreted function, but is expanded only once (during compilation) for a compiled function. If the macro definition has side effects, they will work differently depending on how many times the macro is expanded.
In particular, constructing objects is a kind of side effect. If the macro is called once, then the objects are constructed only once. In other words, the same structure of objects is used each time the macro call is executed. In interpreted operation, the macro is reexpanded each time, producing a fresh collection of objects each time. Usually this does not matter—the objects have the same contents whether they are shared or not. But if the surrounding program does side effects on the objects, it makes a difference whether they are shared. Here is an example:
(defmacro empty-object () (list 'quote (cons nil nil)))
(defun initialize (condition) (let ((object (empty-object))) (if condition (setcar object condition)) object))
If initialize
is interpreted, a new list (nil)
is
constructed each time initialize
is called. Thus, no side effect
survives between calls. If initialize
is compiled, then the
macro empty-object
is expanded during compilation, producing a
single “constant” (nil)
that is reused and altered each time
initialize
is called.
One way to avoid pathological cases like this is to think of
empty-object
as a funny kind of constant, not as a memory
allocation construct. You wouldn’t use setcar
on a constant such
as '(nil)
, so naturally you won’t use it on (empty-object)
either.
Previous: Eval During Expansion, Up: Problems with Macros [Contents][Index]