Subject: Re: bug#66940: Dynamic scoping is all weird now?

From: Michael Heerdegen <[email-masked]>
Date: Tue, 07 Nov 2023 06:59:44 +0100

Dave Goel <[email-masked]> writes:

> Let's eval this at the start:
>
> (setq lexical-binding nil)
>
> Eval it, so we are operating in the old world of dynamic scoping.

Ok, let's do this.

> (let ((ii 1))
>     (defmacro mac ()
>       `(print ,ii)
>       )
>     (mac))

So now:

  (symbol-function 'mac)
    ==> (macro . (lambda () (list 'print ii)))

> [...]
> So, let's try this -
>
> (setq ii 3)
> (let ((ii 4))
>   (mac))
>
> It evals to 3, not 4. Per our understanding of dynamic scoping, we
> would have expected it use the innermost ii?  Why does it use the
> global ii?

Remember that macros are not expanded on the fly - they are expanded
once for the complete expression in a separate step _before_ the actual
evaluation.  So:

(setq ii 3)
; ii --> 3

Expansion of the second expression:

(let ((ii 4))
  (mac))

  ~~> (let ((ii 4)) (print 3))
                    ^^^^^^^^^
                 expansion of (mac)

Evaluation:

(let ((ii 4)) (print 3))
; prints "3"

A different thing would be to define

     (defmacro mac ()
       '(print ii))

Then the expansion would still reference the variable.  But you could as
well just write (print ii) then.

To sum up: you are using macros wrong in this case.  Macros are not
expanded at run-time.  It is important to remember that.

Note that not even `cl-macrolet' works like this: the definition and
scope of the defined macros is local, but they are still expanded before
evaluation, so the expansion can't refer to run-time bindings as well.

Michael.