elisp: Repeated Expansion

 
 13.5.5 How Many Times is the Macro Expanded?
 --------------------------------------------
 
 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.
 
    Therefore, you should avoid side effects in computation of the macro
 expansion, unless you really know what you are doing.
 
    One special kind of side effect can’t be avoided: constructing Lisp
 objects.  Almost all macro expansions include constructed lists; that is
 the whole point of most macros.  This is usually safe; there is just one
 case where you must be careful: when the object you construct is part of
 a quoted constant in the macro expansion.
 
    If the macro is expanded just once, in compilation, then the object
 is constructed just once, during compilation.  But in interpreted
 execution, the macro is expanded each time the macro call runs, and this
 means a new object is constructed each time.
 
    In most clean Lisp code, this difference won’t matter.  It can matter
 only if you perform side-effects on the objects constructed by the macro
 definition.  Thus, to avoid trouble, *avoid side effects on objects
 constructed by macro definitions*.  Here is an example of how such side
 effects can get you into trouble:
 
      (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.