5

In his book ANSI Common Lisp, p.320, Paul Graham writes of macrolet: "Like flet, the local macros may not call one another."

Maybe I misunderstand this but I can't think of any way in which it could be true. Macros do not call each other so much as expand into each other and the nature of macro expansion is such that it continues until all macros defined in the scope have been expanded away.

Code such as the following disagrees with Graham in every implentation of Common Lisp I've tried:

(macrolet ((jump (x) `(car ,x))
           (skip (x) `(jump ,x))
           (hop (x) `(skip ,x)))
  (hop '(1 2 3))) 

=> 1

(macrolet ((yin (n x)
             (if (zerop n)
                 `(cdr ,x)
                 `(yang ,(1- n) ,x)))
           (yang (n x)
             (if (zerop n)
                 `(car ,x)
                 `(yin ,(1- n) ,x))))
      (yin 6 '(1 2 3)))

=> (2 3)

Is Graham's statement in error?

user3414663
  • 531
  • 3
  • 11
  • This statement is questionable given the [ANSI spec](http://www.lispworks.com/documentation/HyperSpec/Body/s_flet_.htm), as it doesn't specify anything about `macrolet` definitions being mutually recursive or not. However, given what is described [in CTLT2](https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node83.html), you can understand the historic context. – acelent Aug 31 '15 at 10:38

1 Answers1

8

It is okay for a macro defined by macrolet to expand into the use of a different macro defined in the same macrolet. It is incorrect for a macro defined by macrolet to directly use a different macro defined in the same macrolet. For example:

(macrolet ((jump (x) `(car ,x))
           ;; Okay since skip expands into jump.
           (skip (x) `(jump ,x)))
  (skip '(1 2 3))) 

=> 1

as opposed to

(macrolet ((jump (x) `(car ,x))
           ;; Wrong since skip uses jump directly.
           (skip (x) (jump x)))
  (skip '(1 2 3))) 

=> Error: The function COMMON-LISP-USER::JUMP is undefined.
malisper
  • 1,671
  • 1
  • 12
  • 16
  • 1
    I think it's not quite an error, has undefined consequences: "The macro-expansion functions defined by macrolet are defined in the lexical environment in which the macrolet form appears. Declarations and macrolet and symbol-macrolet definitions affect the local macro definitions in a macrolet, but *the consequences are undefined if the local macro definitions reference **any local variable or function bindings that are visible in that lexical environment.***" I *think* this would mean that since the macrodefinition functions are in the lexical environment, to reference them would be to... – Joshua Taylor Aug 18 '15 at 16:07
  • ...reference a local variable or function binding visible in that lexical environment. The quoted text is from [Special Operator FLET, LABELS, MACROLET](http://www.lispworks.com/documentation/HyperSpec/Body/s_flet_.htm). – Joshua Taylor Aug 18 '15 at 16:08
  • 1
    @JoshuaTaylor, that's not related, what that paragraph means is that local macros cannot use lexical variables and functions. Basically, it's just explaining a common mistake, where a developer forgets that macros expand to code before runtime, possibly in a separate compile-time phase. In other words, `let`, `let*`, `flet` and `labels` define local variables and functions available for code at runtime, while `macrolet` and `symbol-macrolet` define local macros available for code expansion in a pre-processing phase before runtime. – acelent Aug 31 '15 at 09:52
  • Essentially, the ANSI specification omits the (lack of) recursion ability of `macrolet`. [CLTL2 states that `macrolet` is similar in form to `flet`](https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node83.html), which could explain the non-recursive behavior commonly implemented. – acelent Aug 31 '15 at 09:55