(gensym) 总是唯一的,`(symb#) 不是——为什么?
(gensym) is always unique, `(symb#) is not -- why?
如果我编写一个使用 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
,问题会很明显:
(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
那么,为什么不同?我错过了什么?
用`
引用的语法实际上是reader macro;它后面的形式在评估之前由 reader(将文本转换为 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#)))
如果我编写一个使用 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
,问题会很明显:
(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
那么,为什么不同?我错过了什么?
用`
引用的语法实际上是reader macro;它后面的形式在评估之前由 reader(将文本转换为 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#)))