defmulti 将使用def
用于定义符号的 let 块进行扩展。事实上,返回的表达式defmulti
不会被计算,而是使用let生成的形式,因此,对象成为全局定义的。这会导致在定义 var 之后、创建 multi-fn 并且 var 的根绑定受到影响之前,您的测试条件(何时不成功)成功。您的 defmulti 块从未执行过(when-not 表达式也返回 nil),而是扩展了。
进一步说明:
在这里你可以看到这是如何发生的:
(macroexpand '(defmulti buxx class))
现在您可以看到宏调用将生成的表单:
(clojure.pprint/write (macroexpand '(defmulti buxx class))
:with-dispatch clojure.pprint/code-dispatch)
=>
(let*
[v__4080__auto__ (def buxx)]
(clojure.core/when-not
(clojure.core/and
(.hasRoot v__4080__auto__)
(clojure.core/instance? clojure.lang.MultiFn @v__4080__auto__))
...
这导致(def buux)
被扩展。如果您(def buux)
在您的 repl 中进行评估,您可以进行相同的测试。
来自def的文档字符串:
def 产生 var 本身(不是它的值)。
这意味着,在扩展时,它被替换为(可能未绑定的)var。
因此,在扩展时,def 总是创建一个 var,但返回新值(对于 var)的可选形式将仅在评估扩展的 def 时进行评估。宏和特殊形式将在实际评估之前进行扩展。例如。测试
(defmacro i-have-side-effects
[]
(println "I was invoked!")
42)
(when-not true
(println (i-have-side-effects)))
=>
#'user/i-have-side-effects
I was invoked!
nil
所以可能你不应该有条件地定义一个多方法。