2009-11-21 12 views
16

Aunque he utilizado Clojure, no había examinado las reglas de alcance en detalle. Me estoy poniendo cada vez más confundido mientras leo la documentación. Hice una pequeña prueba para probar las resoluciones del alcance y estoy sorprendido por la complejidad. ¿Podría alguien explicar el propósito y las diversas reglas que usa Clojure?Reglas de alcance en Clojure

(def x 1) 

(defn dummy-fn2[] 
    (+ x 1))   

(defn dummy-fn[] 
    (println "entering function: " x) 
     (let [x 100] 
     (println "after let: " x) 
     (let [x (dummy-fn2)] 
      (println "after let and dummy2: " x) 
      (binding [x 100] 
      (println "after binding: " x) 
      (let [x (dummy-fn2)] 
       (println "after binding and dummy2: " x)))))) 

1:2 foo=> (dummy-fn) 
entering function: 1 
after let: 100 
after let and dummy2: 2 
after binding: 2 
after binding and dummy2: 101 
nil 
+3

Con el fin de evitar la repetición de la documentación, tal vez se podría describir lo que espera y por qué es diferente de lo que realmente sucede . –

Respuesta

14

Clojure utiliza tanto ámbito léxicolet para los símbolos y alcance dinámicobinding para vars el registro de salida documentación del clojure vars.

  • "entering function": ¡bien hasta ahora! el símbolo x se resuelve en la var y esto está tomando la "encuadernación raíz" de la var x.
  • "after let": un enlace local cubrió la var, el símbolo x ahora es 100 no una var.
  • "después de let y dummy2": la x en el muerto-Fn2 se refiere a la var x, por lo que utiliza la unión de x raíz y volvió uno más que eso (+ 1 1)
  • "después de la unión ": ¡complicado! el enlace reemplazó dinámicamente el enlace raíz de la var llamada x (que era 1) con 100, pero el símbolo local x ya no es la var por lo que se obtiene el enlace local.
  • "después de la unión y dummy2": la unión sustituye el valor de la raíz de la var x con 100 y este volvió uno más que eso (+ 100 1)
+0

parece que el enlace reemplaza "la var con nombre x", no "la var que resuelve el símbolo x" ¿es correcto? –

+0

¿Qué significa "la var con nombre x"? –

+0

Cualquiera que devuelva var (resuelve 'x). –

22

let sombras la toplevel Var x con una local x. let no crea un Var ni afecta el nivel de Var; enlaza algún símbolo de manera que las referencias locales a ese símbolo serán reemplazadas por el valor let -bound. let tiene alcance léxico, por lo que sus enlaces solo son visibles dentro del formulario let (no en funciones llamadas desde let).

binding temporalmente (thread-localmente) cambia el valor de toplevel Var x, eso es todo lo que hace. Si hay un enlace let en su lugar, binding no lo ve cuando se decide qué valor cambiar (y las vinculaciones let no son variables y no son modificables, por lo que 'algo bueno o le daría un error'). Y binding no enmascara let. binding tiene un alcance dinámico, por lo que sus efectos en niveles Vars son visibles dentro del formulario binding y en cualquier función que se llame desde el formulario binding.

acceder al valor de la llanura de edad x le dará todo lo que es en la parte superior de la pila de fijaciones, ya sea el más anidada let valor -bound de x (o el parámetro de función llamada x, o algún valor x se sustituye si usa su propia macro u otras posibilidades.), y solo usa el valor actual de la topología Var x de manera predeterminada si no hay otro enlace en su lugar.

Incluso si el nivel superior Var x está enmascarado por un let -bound x, siempre se puede acceder al nivel superior a través de Var @#'x. Prueba esta versión, tal vez que va a hacer más sentido:

(def x 1) 

(defn dummy-fn2[] 
    (println "x from dummy-fn2:" x) 
    (+ x 1)) 

(defn dummy-fn[] 
    (println "entering function:" x) 
    (println "var x:" @#'x) 
    (dummy-fn2) 
    (println "---") 
    (let [x 100] 
    (println "after let:" x) 
    (println "var x:" @#'x) 
    (dummy-fn2) 
    (println "---") 
    (let [x (dummy-fn2)] 
     (println "after let and dummy-fn2:" x) 
     (println "var x:" @#'x) 
     (dummy-fn2) 
     (println "---") 
     (binding [x 888] 
     (println "after binding:" x) 
     (println "var x:" @#'x) 
     (dummy-fn2) 
     (println "---") 
     (let [x (dummy-fn2)] 
      (println "after binding and dummy2:" x) 
      (println "var x:" @#'x) 
      (dummy-fn2) 
      (println "---")))))) 

Da:

entering function: 1 
var x: 1 
x from dummy-fn2: 1 
--- 
after let: 100 
var x: 1 
x from dummy-fn2: 1 
--- 
x from dummy-fn2: 1 
after let and dummy-fn2: 2 
var x: 1 
x from dummy-fn2: 1 
--- 
after binding: 2 
var x: 888 
x from dummy-fn2: 888 
--- 
x from dummy-fn2: 888 
after binding and dummy2: 889 
var x: 888 
x from dummy-fn2: 888 
--- 
+0

obtengo 'IllegalStateException No se puede vincular dinámicamente una var no dinámica 'cuando ejecuto este código. Se resuelve reemplazando la forma 'binding' con' let' o usando '(def ^: dynamic x 1)' – dkinzer