Подтвердить что ты не робот

Clojure и ^: динамический

Я попытался понять динамические переменные и функцию привязки, поэтому я попробовал это (clojure 1.3):

user=> (defn f [] 
           (def ^:dynamic x 5) 
           (defn g [] (println x)) 
           (defn h [] (binding [x 3] (g))) 
           (h))
#'user/f
user=> (f)     
5
nil

Confused, я попробовал этот несколько более простой код:

user=> (def ^:dynamic y 5)
#'user/y
user=> (defn g [] (println y))
#'user/g
user=> (defn h [] (binding [y 3] (g)))
#'user/h
user=> (h)
3
nil

В чем разница между двумя частями кода? Почему второй пример работает, но первый не работает?

Подсказка: я просто понял, что следующие работы (до сих пор не совсем понятны):

user=> (def ^:dynamic y 5)
#'user/y
user=> (defn f [] (defn g [] (println y)) (defn h [] (binding [y 3] (g))) (h))
#'user/f
user=> (f)
3
nil
user=> 
4b9b3361

Ответ 1

Я получаю 3 в результате (как и следовало ожидать), когда я запускаю свой первый пример в Clojure 1.4.... вы пробовали это со свежим REPL?

^:dynamic является инструкцией компилятора Clojure, что символ (как определено с помощью def) предназначен для динамического восстановления (с помощью binding).

Пример:

(def foo 1)
(binding [foo 2] foo)
=> IllegalStateException Can't dynamically bind non-dynamic var: ...

(def ^:dynamic bar 10)
(binding [bar 20] bar)    ;; dynamically bind bar within the scope of the binding
=> 20
bar                       ;; check underlying value of bar (outside the binding)
=> 10

Обратите внимание, что binding имеет динамическую область видимости в вызывающем потоке - любые функции, вызываемые внутри связывания, будут видеть измененное значение bar (20), но любые другие потоки все равно будут видеть неизменное значение корня 10.

Наконец, несколько пунктов стиля, которые могут вам помочь:

  • Обычно считается, что плохая идея заключается в том, чтобы помещать def и defn в функции, поскольку они влияют на охватывающее пространство имен. Внутри функций вы должны использовать (let [foo bar] ...).
  • Когда вы обнаружите, что хотите использовать binding, вам обычно следует рассмотреть вопрос о том, можно ли добиться того же результата, используя функции более высокого порядка. binding полезен в некоторых контекстах, но, как правило, он не является хорошим способом передачи параметров. Функциональная композиция обычно лучше в долгосрочной перспективе. Причиной этого является то, что binding создает неявный контекст, необходимый для выполнения вашей функции, и это может быть трудно проверить/отладить.