2013-05-08 10 views

Odpowiedz

23

znajdę alter-var root bardzo rzadko pojawia się w idiomatycznym kodu Clojure; nie chodzi o to, że coś jest nie w porządku, jest to po prostu przeznaczone na przypadki narożne. Jeśli używasz go do budowania pętli i taki jest znak, że coś wymaga innego podejścia. Zwykle widzę to w procedurach inicjalizacji dla ustawiania poświadczeń dostępu lub rejestratorów i tym podobnych.

alter-var-root wykorzystuje funkcję do mechanicznej zmiany wartości zmiennej, podczas gdy def po prostu ustawia ją na nową wartość. W twoim przykładzie są one równoważne.

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

alter-var-root jest również chce utworzyć nową 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 może pracować na innych nazw, a także:

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> 

Ten ostatni przypadek użycia jest tylko jeden mam kiedykolwiek potrzebne w praktyce. Na przykład zmuszając clojure.logging używać poprawnej slf4j rejestratora jako przykład z projektu palecie:

(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))))) 

który jest po prostu za pomocą alter-var-root zresetować var ​​w innej przestrzeni nazw, niezależnie od jej treści na inicjalizacji. Przypuszczam, że to trochę hack ...

+1

Ale definiowanie var więcej niż jeden raz jest uważane za zły styl, ponieważ 'def' zawsze definiuje vars najwyższego poziomu. Jeśli chcesz zmienić powiązanie jakiegoś var, dobrym wyjściem jest użycie 'alter-var-root'. –

+1

Jako początkujący, czuję, że ta odpowiedź wyjaśnia * kiedy możesz użyć * 'alter-var-root', ale nie ** dlaczego ** używałbyś tego. Jakie są typowe przypadki użycia? –

+0

Będę komentować ten –

15

alter-var-root zapewnia wartość dodaną bycia atomowym w odniesieniu do aplikacji funkcji. Dwa (prawdopodobnie współbieżne) aplikacje z zakresu (alter-var-root #'foo inc) gwarantują, że foo wzrośnie o 2.

Z (def x (inc x)) nie ma takiej gwarancji. Może to zastąpić wszelkie zmiany dokonane przez inne wątki między odczytaniem wartości x i zapisaniem jej zaktualizowanej wartości.

Z drugiej strony, jeśli używasz alter-var-root dla swojej atomowości, to być może atomy są lepsze dla twojego przypadku użycia niż vary.

+0

Nie ma gwarancji atomowości dla obu konstrukcji. – amalloy

+0

A może mam na myśli z-redefs ... mam link do gwarancji? – amalloy

+1

@amalloy 'alter-var-root' deleguje do' clojure.lang.Var.alterRoot', który jest 'zsynchronizowany'. 'bindRoot' jest również' zsynchronizowany', ale oczywiście przyjmuje nową wartość root jako argument, który musi być więc gotowy przed próbą uzyskania blokady. –

11

Z 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's 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" 

Z 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's root binding (identified by symbol w) 

; => [1 2 4 5 3 0 7 8 6] 
Powiązane problemy