If I write a macro that uses the symb#
shortcut to create a gensym which is then bound as a global variable, the exact same symbol gets generated over and over. However, it functions correctly if I call gensym
manually. Very simple examples:
(defmacro indirection
[name & body]
`(do (def name# ~@body)
(defn ~name [] name#)))
(indirection foo 3)
(foo) ; ⇒ 3
(indirection goo 5)
(goo) ; ⇒ 5
(foo) ; ⇒ 5
The problem is apparent if you use macroexpand
:
(macroexpand '(indirection foo 3))
(do (def name__2863__auto__ 3) (clojure.core/defn foo [] name__2863__auto__))
(macroexpand '(indirection foo 3))
(do (def name__2863__auto__ 3) (clojure.core/defn foo [] name__2863__auto__))
This problem goes away if I call gensym
the long way:
(defmacro redirection
[name & body]
(let [rename (gensym)]
`(do (def ~rename ~@body)
(defn ~name [] ~rename))))
(redirection koo 3)
(koo) ; ⇒ 3
(redirection moo 5)
(moo) ; ⇒ 5
(koo) ; ⇒ 3
So, why the difference? What am I missing?
Syntax quoting with `
is actually a reader macro; the form that follows it is transformed by the reader (which translates text to Clojure forms) prior to evaluation. This means that any symbol ending in #
within the syntax-quoting is translated to an autogenerated symbol only once, when the text is first read; that autogenerated symbol is then inserted directly into the macro definition, and appears verbatim in the macroexpanion every time that macro is invoked. This can be illustrated easily at the REPL:
user=> `(foo bar#)
(user/foo bar__2288__auto__)
user=> `(foo bar#)
(user/foo bar__2291__auto__)
The typical use case for auto-gen'ed symbols with #
is to define local variables inside a quoted let
or fn
form. There, it does not matter that the same symbol is re-used for multiple macro invocations; it only needs to be unique within each invocation. For instance:
(defmacro indirection
[name body]
`(let [name# ~body]
(defn ~name [] name#)))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With