tutorial online example clojure

clojure - online - Diferencia entre usar "def" para actualizar una var y "alter-var-root"



clojure windows (3)

¿Cuál es la diferencia entre usar "def" para actualizar una var y usar "alter-var-root"? p.ej

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

vs

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


Con 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"

Con 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]


Encuentro que alter-var-root rara vez aparece en el código idiomático de Clojure; no es que haya nada malo en ello, solo está destinado a casos de esquina. Si lo usa para crear bucles y, por lo tanto, es una señal de que se necesita un enfoque diferente. Principalmente lo veo en las rutinas de inicialización para configurar credenciales de acceso o registradores y demás.

alter-var-root usa una función para cambiar mecánicamente el valor de var mientras que def simplemente lo establece en un nuevo valor. En tu ejemplo son equivalentes.

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

alter-var-root tampoco está dispuesto a crear una nueva 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 puede funcionar en otros espacios de nombres también:

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>

Este último caso de uso es el único que he necesitado en la práctica. Por ejemplo, forzando a clojure.logging a usar el registrador slf4j correcto como un ejemplo del proyecto Pallet:

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

Lo cual es solo usar alter-var-root para restablecer una var en otro espacio de nombres, independientemente de su contenido en la inicialización. Supongo que es un truco ...


alter-var-root proporciona el valor agregado de ser atómico con respecto a la aplicación de la función. Dos aplicaciones (posiblemente concurrentes) de (alter-var-root #''foo inc) garantizan que foo aumentará en 2.

Con (def x (inc x)) no existe tal garantía. Puede sobrescribir cualquier cambio realizado por otros subprocesos entre leer el valor de x y escribir su valor actualizado.

Por otro lado, si está utilizando alter-var-root para su atomicidad, quizás los átomos sean mejores para su caso de uso que los vars.