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 defnel 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 futilizza ancora l'associazione x = 1che era nell'ambito nell'ambito della definizione, ovvero l'associazione val x = 100non 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 defcostrutto 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.