Clojure (gensym)总是唯一的,`(symb#)不是——为什么?

Clojure (gensym)总是唯一的,`(symb#)不是——为什么?,clojure,Clojure,如果我编写一个宏,使用symb#快捷方式创建一个gensym,然后将其绑定为一个全局变量,则会反复生成完全相同的符号。但是,如果我手动调用gensym,它会正常工作。非常简单的例子: (defmacro indirection [name & body] `(do (def name# ~@body) (defn ~name [] name#))) (indirection foo 3) (foo) ; ⇒ 3 (indirection goo 5)

如果我编写一个宏,使用
symb#
快捷方式创建一个gensym,然后将其绑定为一个全局变量,则会反复生成完全相同的符号。但是,如果我手动调用
gensym
,它会正常工作。非常简单的例子:

(defmacro indirection
    [name & body]
    `(do (def name# ~@body)
         (defn ~name [] name#)))

(indirection foo 3)
(foo) ; ⇒ 3
(indirection goo 5)
(goo) ; ⇒ 5
(foo) ; ⇒ 5
如果使用
宏扩展
,问题很明显:

(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__))
如果我长途呼叫gensym,这个问题就会消失:

(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

那么,为什么会有区别呢?我遗漏了什么?

`
引用语法实际上是一种错误;在评估之前,读者(将文本翻译成Clojure形式)会对随后的形式进行转换。这意味着在第一次读取文本时,语法引号中以
#
结尾的任何符号仅转换为自动生成的符号一次;然后,自动生成的符号将直接插入宏定义中,并在每次调用宏时逐字显示在宏表达式中。这可以在REPL上很容易地说明:

user=> `(foo bar#)
(user/foo bar__2288__auto__)
user=> `(foo bar#)
(user/foo bar__2291__auto__)
带有
#
的自动生成符号的典型用例是在引用的
let
fn
表单中定义局部变量。在这里,相同的符号是否被重复用于多个宏调用并不重要;它只需要在每次调用中是唯一的。例如:

(defmacro indirection
  [name body]
  `(let [name# ~body]
     (defn ~name [] name#)))

foo#
本身是一种在宏定义被处理之前就被替换的宏吗?啊哈。我很接近,但我关注的是
foo
,而不是“代码”。非常感谢!很好的解释。在理解clojure中宏的道路上又迈出了一步:)