clojure dynamic-scope

clojure y ^: dinámico



dynamic-scope (1)

Intenté entender las variables dinámicas y la función de enlace, así que probé esto (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

Confundido, probé este código algo más simple:

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

¿Cuál es la diferencia entre las dos piezas de código? ¿Por qué el segundo ejemplo funciona pero el primero no?

Sugerencia: Me acabo de dar cuenta de que funciona lo siguiente (aún no entiendo por qué):

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=>


Obtuve 3 como resultado (como es de esperar) cuando ejecuté su primer ejemplo en Clojure 1.4 ... ¿ha intentado esto con un nuevo REPL?

^:dynamic es una instrucción para el compilador Clojure de que un símbolo (como se define con def ) está diseñado para ser rebotado dinámicamente (con binding ).

Ejemplo:

(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

Tenga en cuenta que el binding tiene un alcance dinámico en el subproceso de llamada: cualquier función llamada dentro del enlace verá el valor modificado de la bar (20), pero cualquier otro subproceso seguirá viendo el valor de la raíz sin cambios de 10.

Finalmente, un par de puntos de estilo que pueden ser útiles:

  • En general, se considera una mala idea poner def y defn dentro de las funciones, ya que afectan el espacio de nombres que las contiene. Dentro de las funciones deberías usar (let [foo bar] ...) lugar.
  • Cuando tenga ganas de utilizar el binding , normalmente debería considerar si puede lograr el mismo resultado utilizando funciones de orden superior. binding es útil en algunos contextos, pero en general no es una buena manera de pasar parámetros alrededor de la composición de la función, por lo general, es mejor a largo plazo. La razón de esto es que el binding crea un contexto implícito que se requiere para la ejecución de su función y esto puede ser difícil de probar / depurar.