为什么在defmulti
内执行when-not
后,先前未解析的符号解析得很好,但没有绑定到值?
user=> (resolve 'buux)
nil
user=> (when-not (resolve 'buux) (defmulti buux class))
nil
user=> (resolve 'buux)
#'user/buux
user=> (bound? #'buux)
false
user=> (defmulti buux class)
#'user/buux
user=> (bound? #'buux)
true
答案 0 :(得分:2)
defmulti将使用let-block进行扩展,该let-block使用def
来定义符号。事实上,defmulti
返回的表达式将不会被评估,但它将使用let生成为表单。因此,对象将全局定义。在创建multi-fn并影响var的根绑定之前,这会导致您的测试条件(如果不是)在定义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)
被扩展。如果您在repl中评估(def buux)
,则可以进行相同的测试。
来自def:
的文档字符串def会产生var本身(而不是它的值)。
这意味着,在展开时,它将被替换为(可能未绑定的)var。
因此,在展开时,def始终会创建一个var,但只有在评估展开的def时才会评估返回新值(对于var)的可选表单。宏和特殊表单将在实际评估之前进行扩展。 E. g。用
进行测试(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
所以你可能不应该有条件地定义一个多方法。