是否有一个等效于def的Clojure宏?

cfg*_*uss 2 macros function clojure

我想编写一个宏,sym-def该宏具有与特殊形式相同的行为,def但是使用(symbol "c"),例如,作为第一个参数。

我的第一步是

(def (symbol "c") 4)
Run Code Online (Sandbox Code Playgroud)

但这返回了错误First argument to def must be a Symbol

我的第二步是

(eval `(def ~(symbol "c") 4))
Run Code Online (Sandbox Code Playgroud)

c在全球环境中成功将其定义为4。为什么第二步成功却第一步失败了?

最后,我尝试编写所需的宏

(def (symbol "c") 4)
Run Code Online (Sandbox Code Playgroud)

但这有一个“坏” macroexpand

(eval `(def ~(symbol "c") 4))
Run Code Online (Sandbox Code Playgroud)

以便

(defmacro sym-def [sym value] `(def ~sym ~value))
Run Code Online (Sandbox Code Playgroud)

失败,出现与我第一步相同的错误。

编写宏的正确方法是什么?

ama*_*loy 9

def不评估其第一个参数。想象一下,如果发生的话,将会造成混乱!你不会写

(def x 1)
Run Code Online (Sandbox Code Playgroud)

因为它首先会尝试求值x,而由于x尚未定义而失败!现在,由于它不评估其参数,因此很明显

(def (symbol "c") 4)
Run Code Online (Sandbox Code Playgroud)

不起作用,就像

(def 'c 4)
Run Code Online (Sandbox Code Playgroud)

不会。def要求其第一个参数为文字符号。您没有文字符号,因此无法使用def

但是,存在一种较低级别的机制来与名称空间中的映射进行交互。在这种情况下,您需要clojure.core/intern

(intern *ns* (symbol "c") 4)
Run Code Online (Sandbox Code Playgroud)

intern是一个普通函数,因此它会评估其所有参数,这意味着您可以用任何想要的疯狂方式来构造var名称。然后,它将给您的符号映射到所需值的变量添加到给定的名称空间。