when-not和/或defmulti的好奇行为

时间:2013-09-07 21:59:13

标签: clojure symbols

为什么在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

1 个答案:

答案 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

所以你可能不应该有条件地定义一个多方法。