Jak już zauważyłeś, fakt, że w Clojure odradza się zmienność, nie oznacza, że jest zabroniony i nie ma żadnych konstrukcji, które by to wspierały. Masz więc rację, że używając def
możesz zmienić / mutować powiązanie w środowisku w sposób podobny do przypisania w innych językach (zobacz dokumentację Clojure na temat vars ). Zmieniając powiązania w środowisku globalnym, zmieniasz także obiekty danych korzystające z tych powiązań. Na przykład:
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
Zauważ, że po ponownym zdefiniowaniu wiązania x
funkcja f
również się zmieniła, ponieważ jego ciało używa tego wiązania.
Porównaj to z językami, w których redefinicja zmiennej nie usuwa starego powiązania, a jedynie zacienia go, tzn. Czyni go niewidocznym w zakresie, który pojawia się po nowej definicji. Zobacz, co się stanie, jeśli napiszesz ten sam kod w 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
Zauważ, że po drugiej definicji x
funkcja f
nadal używa powiązania, x = 1
które było w zasięgu, gdy zostało zdefiniowane, tzn. Powiązanie val x = 100
nie zastępuje poprzedniego powiązania val x = 1
.
Konkluzja: Clojure pozwala mutować globalne środowisko i redefiniować w nim powiązania. Można by tego uniknąć, tak jak robią to inne języki, takie jak SML, ale def
konstrukcja w Clojure ma na celu uzyskiwanie dostępu i mutowanie środowiska globalnego. W praktyce jest to bardzo podobne do tego, co przypisanie może zrobić w imperatywnych językach, takich jak Java, C ++, Python.
Mimo to Clojure zapewnia wiele konstrukcji i bibliotek, które unikają mutacji, i możesz przejść długą drogę, nie używając go wcale. Unikanie mutacji jest zdecydowanie preferowanym stylem programowania w Clojure.