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

Разница между использованием "def" для обновления var и "alter-var-root"

В чем разница между использованием "def" для обновления var и использованием "alter-var-root"? например.

(def x 3)
(def x (inc x))

против

(def x 3)
(alter-var-root #'x inc)
4b9b3361

Ответ 1

Я нахожу, что alter-var-root очень редко встречается в идиоматическом коде Clojure; не то, что с ним что-то не так, он просто предназначен для угловых случаев. Если вы обнаружите, что используете его для создания циклов, и это означает, что что-то требует другого подхода. В основном я вижу его в процедурах инициализации для установки учетных данных доступа или регистраторов и т.д.

alter-var-root использует функцию для механического изменения значения var, а def просто устанавливает его в новое значение. В вашем примере они эквивалентны.

hello.exp> (def foo 4)
#'hello.exp/foo
hello.exp> (alter-var-root #'foo inc)
5
hello.exp> foo
5

alter-var-root также не хочет создавать новый var:

hello.exp> (alter-var-root #'foo1 inc) 
CompilerException java.lang.RuntimeException: Unable to resolve var: foo1 in this context, compiling:(NO_SOURCE_PATH:1) 

alter-var-root может работать и с другими пространствами имен:

hello.exp> (in-ns 'user)
#<Namespace user> 
user> (alter-var-root #'hello.exp/foo inc) 
 6
user> (def hello.exp/foo 4)
CompilerException java.lang.RuntimeException: Can't create defs outside of current ns, compiling:(NO_SOURCE_PATH:1)
user>

Этот последний случай использования - единственный, который мне когда-либо понадобился на практике. Например, принудительно использовать clojure.logging для использования правильного регистратора slf4j в качестве примера из проекта поддона:

(defn force-slf4j
  "The repl task brings in commons-logging, which messes up our logging
   configuration. This is an attempt to restore sanity."
   []
  (binding [*ns* (the-ns 'clojure.tools.logging.slf4j)]
    (alter-var-root
     #'clojure.tools.logging/*logger-factory*
     (constantly (clojure.tools.logging.slf4j/load-factory)))))

Который использует alter-var-root to reset var в другом пространстве имен независимо от его содержимого при инициализации. Я полагаю, это немного взломать...

Ответ 2

alter-var-root обеспечивает добавленную стоимость атомарного использования приложения-функции. Два (возможно одновременных) приложения (alter-var-root #'foo inc) гарантируют, что foo будет увеличиваться на 2.

С (def x (inc x)) такой гарантии нет. Он может перезаписать любые изменения, сделанные другими потоками, между чтением значения x и записью его обновленного значения.

С другой стороны, если вы используете alter-var-root для его атомарности, то, возможно, атомы лучше для вашего использования, чем vars.

Ответ 3

С def:

(def w (vector))        ; create Var named w and bind it to an empty vector
(dotimes [x 9]          ; repeat 9 times (keeping iteration number in x):
 (future                ;  execute in other thread:
  (def w                ;   replace root binding of w with
    (conj w             ;    a new vector with all elements from previous (w)
          x))))         ;     with added an element indicating current iteration (x) 

w                       ; get a value of Var root binding (identified by symbol w)

; => [0 2 3 6 8 7 4 5]  ; 1 is missing !!!
                        ; second thread overlapped with another thread
                        ; during read-conjoin-update and the other thread "won"

С alter-var-root:

(def w (vector))        ; create Var named w and bind it to an empty vector
(dotimes [x 9]          ; repeat 9 times (keeping iteration number in x):
 (future                ;  execute in other thread:
  (alter-var-root #'w   ;   atomically alter root binding of w
   (fn [old]            ;    by applying the result of a function,
    (conj               ;     that returns a new vector
     old                ;      containing all elements from previous (w)
     x)))))             ;      with added an element indicating current iteration (x) 

w                       ; get a value of Var root binding (identified by symbol w)

; => [1 2 4 5 3 0 7 8 6]