Clojure: привязка против with-redefs

clojure.core содержит макросы bindings и with-redefs. Глядя на строки документации и примеры на clojuredocs.org, кажется, что они делают что-то очень похожее. В чем разница и какой из них я должен использовать в каких ситуациях?


person Miikka    schedule 22.11.2013    source источник
comment
Также см. этот вопрос: 15748334#15748334" title="какой смысл определять что-то как динамическое, когда вам не нужно определять"> stackoverflow.com/questions/15747774/   -  person Alex    schedule 22.11.2013


Ответы (1)


Clojure Vars может иметь локальные привязки потока. binding использует их, в то время как with-redefs фактически изменяет корневую привязку (которая чем-то похожа на значение по умолчанию) var.

Еще одно отличие состоит в том, что binding работает только для :dynamic переменных, а with-redefs работает для всех переменных.

Примеры:

user=> (def ^:dynamic *a* 1)
#'user/*a*
user=> (binding [*a* 2] *a*)
2
user=> (with-redefs [*a* 2] *a*)
2
user=> (binding [*a* 2] (doto (Thread. (fn [] (println "*a* is " *a*))) (.start) (.join)))
*a* is  1
#<Thread Thread[Thread-2,5,]>
user=> (with-redefs [*a* 2] (doto (Thread. (fn [] (println "*a* is " *a*))) (.start) (.join)))
*a* is  2
#<Thread Thread[Thread-3,5,]>

Вы можете использовать (недокументированный) binding-conveyor-fn для передачи локальных привязок потока в новые потоки:

user=> (binding [*a* 2] (doto (Thread. (#'clojure.core/binding-conveyor-fn (fn [] (println "*a* is " *a*)))) (.start) (.join)))
*a* is  2
#<Thread Thread[Thread-5,5,]>
person opqdonut    schedule 22.11.2013
comment
Именно поэтому with-redefs предназначен для использования в тестах (где вы можете захотеть войти и заглушить функцию), а binding также может быть полезен в рабочем коде. - person Peeja; 29.09.2014
comment
@Peeja, спасибо, другими словами, with-redefs никогда не следует использовать в многопоточных контекстах? - person Erik Kaplun; 11.06.2019
comment
@opqdonut по состоянию на июнь 2019 года в документации Vars есть раздел Обязательная передача, в котором выделены future, send , send-off и pmap как имеющие возможности связывания перевозки — значит ли это, что binding-conveyor-fn больше не нужен? - person Erik Kaplun; 11.06.2019
comment
@ErikKaplun, binding-conveyor-fn не нужен, если вы используете упомянутые вами функции. Однако, если вы создаете потоки с помощью (Thread. ...), он вам все равно понадобится. - person Miikka; 14.01.2020