2013-09-07 2 views
0

Почему происходит то, что после того, как я выполнил defmulti в пределах when-not, ранее неразрешенный символ разрешает штраф, но не привязан к значению?Любопытное поведение if-not и/или defmulti

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

@Rainer Joswing, Clojure - это Lisp. Зачем удалять тег? – missingfaktor

+0

Clojure - это язык, основанный на Lisp. вопрос настолько Clojure, что маловероятно, что любой, скажем, пользователь Emacs Lisp мог бы внести свой вклад в это. Я также не буду отмечать специальные вопросы C++ с помощью тега Algol или C. Операторы, о которых вы упомянули, defmulti и when-not также не являются особыми для Clojure, а также последствия их использования. –

ответ

2

defmulti будет расширен с LET-блоком, который использует def для определения символа. На самом деле выражение, возвращаемое defmulti, не будет оцениваться, но оно будет сгенерировано как форма, используя let. Таким образом, объект становится глобальным. Это приводит к тому, что ваше тестовое условие (если оно не удастся) преуспеть после того, как было определено var, до создания мультифайла и повреждение корневого каталога var. Ваш defmulti-блок никогда не исполнялся (также когда-не выражение, возвращенное 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, вы можете сделать те же тесты.

С ДЕФа строку документации:

Защиту дает сам вар (а не его значение).

Это означает, что при его расширении он заменяется (возможно, несвязанным) var.

Поэтому при расширении def всегда создает var, но необязательная форма, которая возвращает новое значение (для var), будет оцениваться только при вычислении расширенного def. Макросы и специальные формы будут расширены до их фактической оценки. 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 

Так что, вероятно, вы не должны определять мульти-метод условно в любом случае.

+0

Я не понимаю этого: «def всегда создает привязку, но необязательная форма, которая возвращает новое значение (для var), будет оцениваться только при вызове расширенного def.» - что это значит? – missingfaktor

+0

Что делать, если мне нужна эта точная функциональность в том, что я пишу? – missingfaktor

+0

Question1: Это означает, что всякий раз, когда вы используете def в коде, который будет прочитан, var будет создан. Это не обязательно будет связано, в зависимости от того, будет ли эта часть кода оценена. Вопрос2: Вы должны использовать другой тест, чем 'resolve'. Рассмотрите возможность написания модифицированной версии 'defmulti'. https://github.com/clojure/clojure/blob/c6756a8bab137128c8119add29a25b0a88509900/src/clj/clojure/core.clj#L1584 –

Смежные вопросы