在 Clojure 中,表达式的结果可以作为 (def) 的第一个参数吗?

In Clojure, can the result of an expression be the first argument to a (def)?

我的最终目标是用宏生成这段代码,其中单词可以是任何字符串(没有白色 space):

=> (def word "word")

这就是我目前所拥有的。它编译,但不 运行.

=> (defmacro mirror [val] `(def (symbol val) val))

预期行为:

=> (mirror "michael")
=> (mirror "jackson")
=> michael
"michael"
=> jackson
"jackson"

实际行为:

=> (mirror "michael")

Syntax error compiling def at (/tmp/form-init2235651799765014686.clj:1:25).
First argument to def must be a Symbol

据我了解,(def) 需要一个 Symbol 作为第一个参数,(symbol val) 应该 return。我的猜测是编译器在评估它们之前正在检查 (def)'s arguments' 类型,因此它看到的不是符号而是表达式并抛出错误。

(defmacro mirror [val] `(def ~(symbol val) ~val))

如果希望val是一个字符串文字,akond的解决方案就足够了。

如果你想使用字符串生成表达式,就像 amalloy 在他的评论中所做的那样,你可以这样写:

(defmacro mirror [exp] (let [val (eval exp)] `(def ~(symbol val) ~val)))

使用 amalloy 的例子我们有:

=> (def module-name "foo")
#'user/module-name
=> (mirror (str module-name "-test"))
#'user/foo-test
=> foo-test
"foo-test"

此宏计算表达式 "at compile-time"。这意味着您不能在其中使用局部变量(例如 (let [suffix "-test"] (mirror (str module-name suffix))) 失败)。要评估它 "at run-time" 你可以这样写:

(defmacro mirror [exp] `(let [~'val ~exp] (eval `(def ~(symbol ~'val) ~~'val))))

或者,您可以避免使用宏和 def,而使用 intern