clojure y^: dinámica


Traté de 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?

Pista: Acabo de darme cuenta de que las siguientes obras (todavía no entiendo completamente 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=> 
Author: Matt Fenwick, 2012-07-31

1 answers

Obtengo 3 como resultado (como es de esperar) cuando corro tu primer ejemplo en Clojure 1.4.... ¿has probado esto con un REPL fresco?

^:dynamic es una instrucción para el compilador de Clojure que un símbolo (como se define con def) está destinado a ser dinámicamente rebotado (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 binding tiene un ámbito dinámico dentro del subproceso de llamada-cualquier función llamada dentro del enlace verá el valor modificado de bar (20), pero cualquier otro subproceso todavía verá el valor raíz sin cambios de 10.

Finalmente un par de puntos de estilo que puede encontrar útiles:

  • Generalmente se considera mala idea poner def y defn dentro de las funciones, ya que afectan al espacio de nombres que encierra. Dentro de las funciones debe usar (let [foo bar] ...) en su lugar.
  • Cuando te encuentres queriendo usar binding normalmente deberías considerar si puedes lograr el mismo resultado usando funciones de orden superior en su lugar. binding es útil en algunos contextos pero no es en general una buena manera de pasar parámetros alrededor-la composición de la función es generalmente mejor en el largo plazo. La razón de esto es que 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.
 28
Author: mikera,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-07 19:13:49