Como você já notou, o fato de a mutabilidade ser desencorajada no Clojure não significa que ela é proibida e que não há construções que a suportem. Portanto, você está certo que o uso def
pode alterar / alterar uma ligação no ambiente de maneira semelhante à atribuição feita em outros idiomas (consulte a documentação do Clojure em vars ). Ao alterar as ligações no ambiente global, você também altera os objetos de dados que usam essas ligações. Por exemplo:
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
Observe que, após redefinir a ligação para x
, a função f
também mudou, porque seu corpo usa essa ligação.
Compare isso com os idiomas nos quais a redefinição de uma variável não exclui a ligação antiga, mas apenas a obscurece , ou seja, torna-a invisível no escopo que vem após a nova definição. Veja o que acontece se você escrever o mesmo código no 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
Observe que após a segunda definição de x
, a função f
ainda usa a ligação x = 1
que estava no escopo quando foi definida, ou seja, a ligação val x = 100
não substitui a ligação anterior val x = 1
.
Bottomline: Clojure permite alterar o ambiente global e redefinir as ligações nele. Seria possível evitar isso, como outros idiomas, como o SML, mas a def
construção no Clojure tem o objetivo de acessar e alterar um ambiente global. Na prática, isso é muito semelhante ao que a atribuição pode fazer em linguagens imperativas como Java, C ++, Python.
Ainda assim, o Clojure fornece muitas construções e bibliotecas que evitam a mutação, e você pode percorrer um longo caminho sem usá-lo. Evitar a mutação é de longe o estilo de programação preferido no Clojure.