Subject: Re: bug#76132: Clojure-style auto-gensyms for macros

From: Stefan Monnier <[email-masked]>
Date: Mon, 10 Feb 2025 09:21:03 -0500

>> [ Admittedly, this all depends on when `with-uninterned-symbols` is
>>   macro-expanded, but in most cases it will be macro-expanded once and
>>   for all when the macro is defined.  ]
> Well, my naive assumption about macro expansion is that it's done lazily
> from inside-out.

Interesting: that's impossible.
Think about a case like:

    (dolist (push pushes) (message "%S" `(pop ,push)))

how can the macro expander decide whether or not `(push pushes)` and
`(pop ,push)` are calls to macros `push` and `pop` without first
macro-expanding `dolist` and backquote?

Similarly, we can't compile the above code without first expanding all
the macros.

> And furthermore, why does
>
> (defmacro th/my-foo (exp)
>   (let ((x (make-symbol "x")))
>     `(let ((,x 6))
>        (+ ,x ,exp))))
>
> not exhibit the exactly same problem?

Because the above code says explicitly that `make-symbol` is called
every time a call to `th/my-foo` is expanded.  Whereas in your version,
the code says that `make-symbol` is called every time a call to
`with-uninterned-symbols` is expanded, but that can happen either when
we define `my-foo` or when "use" my-foo (depending on whether the
macroexpansion is done lazily or eagerly, and it can't be done lazily
if we compile the macro).

> Is there a way to fix my macro?

I don't think so, in general.  E.g. if you want to generate fresh new
uninterned symbols every time the code is executed, then you can't
compile something like

      (with-uninterned-symbols
        (let ((x$ 6))
          (+ x$ exp)))

since the name of the variable is not known until runtime (and will be
different each time).

I think to fix it, you have to fuse your macro with backquote, so users
would write

    (defmacro my-other-foo (exp)
      (uninterned-backquote
       (let ((x$ 6))
         (+ x$ ,exp))))

but of course you could still support the syntax

    (defmacro my-foo (exp)
      (with-uninterned-symbols
       `(let ((x$ 6))
          (+ x$ ,exp))))

and simply have `with-uninterned-symbols` check that its argument is of
the form `(...) and treat it as a use of `uninterned-backquote`.

        Stefan