Como ya notó, el hecho de que se desaconseja la mutabilidad en Clojure no significa que esté prohibido y que no haya construcciones que lo respalden. Entonces tiene razón en que al usarlo def
puede cambiar / mutar un enlace en el entorno de forma similar a lo que hace la asignación en otros idiomas (consulte la documentación de Clojure en vars ). Al cambiar los enlaces en el entorno global, también cambia los objetos de datos que usan estos enlaces. Por ejemplo:
user=> (def x 1)
#'user/x
user=> (defn f [y] (+ x y))
#'user/f
user=> (f 1)
2
user=> (def x 100)
#'user/x
user=> (f 1)
101
Tenga en cuenta que después de redefinir el enlace para x
, la función también f
ha cambiado, porque su cuerpo usa ese enlace.
Compare esto con los idiomas en los que la redefinición de una variable no elimina el enlace anterior, sino que solo lo sombrea , es decir, lo hace invisible en el alcance que viene después de la nueva definición. Vea lo que sucede si escribe el mismo código en SML REPL:
- val x = 1;
val x = 1 : int
- fun f y = x + y;
val f = fn : int -> int
- f 1;
val it = 2 : int
- val x = 100;
val x = 100 : int
- f 1;
val it = 2 : int
Tenga en cuenta que después de la segunda definición de x
, la función f
todavía utiliza el enlace x = 1
que estaba en el alcance cuando se definió, es decir, el enlace val x = 100
no sobrescribe el enlace anterior val x = 1
.
En pocas palabras: Clojure permite mutar el entorno global y redefinir los enlaces en él. Sería posible evitar esto, como lo hacen otros lenguajes como SML, pero la def
construcción en Clojure está destinada a acceder y mutar un entorno global. En la práctica, esto es muy similar a lo que la asignación puede hacer en lenguajes imperativos como Java, C ++, Python.
Aún así, Clojure proporciona muchas construcciones y bibliotecas que evitan la mutación, y puede recorrer un largo camino sin usarlo en absoluto. Evitar la mutación es, con mucho, el estilo de programación preferido en Clojure.