Come hai già notato, il fatto che la mutevolezza sia scoraggiata in Clojure non significa che sia vietata e che non vi siano costrutti che la supportano. Quindi hai ragione def
nel dire che usando puoi cambiare / mutare un legame nell'ambiente in modo simile a quello che il compito fa in altre lingue (vedi la documentazione di Clojure su vari ). Modificando i bind nell'ambiente globale si cambiano anche gli oggetti dati che usano questi bind. Per esempio:
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
Si noti che dopo aver ridefinito l'associazione per x
, anche la funzione f
è cambiata, poiché il suo corpo utilizza quell'associazione.
Confrontalo con le lingue in cui la ridefinizione di una variabile non cancella la vecchia rilegatura ma la ombreggia , cioè la rende invisibile nell'ambito che segue la nuova definizione. Vedi cosa succede se scrivi lo stesso codice nel REPL SML:
- 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
Si noti che dopo la seconda definizione di x
, la funzione f
utilizza ancora l'associazione x = 1
che era nell'ambito nell'ambito della definizione, ovvero l'associazione val x = 100
non sovrascrive l'associazione precedente val x = 1
.
Bottomline: Clojure permette di mutare l'ambiente globale e ridefinire i vincoli in esso. Sarebbe possibile evitarlo, come fanno altre lingue come SML, ma il def
costrutto in Clojure è pensato per accedere e mutare un ambiente globale. In pratica, questo è molto simile a quello che può fare un compito in linguaggi imperativi come Java, C ++, Python.
Tuttavia, Clojure offre molti costrutti e librerie che evitano la mutazione, e puoi fare molta strada senza usarla affatto. Evitare la mutazione è di gran lunga lo stile di programmazione preferito in Clojure.