Как вы уже заметили, тот факт, что изменчивость в Clojure не рекомендуется, не означает, что она запрещена и что нет конструкций, поддерживающих ее. Таким образом, вы правы в том, что, используя, def
вы можете изменять / изменять мутирование в среде так же, как это делает назначение на других языках (см. Документацию Clojure по vars ). Изменяя привязки в глобальной среде, вы также изменяете объекты данных, которые используют эти привязки. Например:
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
Обратите внимание, что после переопределения привязки x
функция f
также изменилась, поскольку ее тело использует эту привязку.
Сравните это с языками, в которых переопределение переменной не удаляет старую привязку, а только затеняет ее, т. Е. Делает ее невидимой в области видимости, следующей за новым определением. Посмотрите, что произойдет, если вы напишите тот же код в 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
Обратите внимание, что после второго определения x
, функция f
все еще использует привязку, x = 1
которая была в области действия, когда она была определена, то есть привязка val x = 100
не перезаписывает предыдущую привязку val x = 1
.
Итог: Clojure позволяет изменять глобальную среду и переопределять привязки в ней. Можно было бы избежать этого, как это делают другие языки, такие как SML, но def
конструкция в Clojure предназначена для доступа и изменения глобальной среды. На практике это очень похоже на то, что назначение может делать в императивных языках, таких как Java, C ++, Python.
Тем не менее, Clojure предоставляет множество конструкций и библиотек, которые избегают мутаций, и вы можете пройти долгий путь, не используя его вообще. Избежание мутаций - безусловно предпочтительный стиль программирования в Clojure.