Wie Sie bereits bemerkt haben, bedeutet die Tatsache, dass von Veränderlichkeit in Clojure abgeraten wird, nicht, dass dies verboten ist und dass es keine Konstrukte gibt, die dies unterstützen. Sie haben also Recht, dass def
Sie mit einer Bindung eine Bindung in der Umgebung ähnlich wie die Zuweisung in anderen Sprachen ändern / mutieren können (siehe die Clojure-Dokumentation zu vars ). Durch Ändern von Bindungen in der globalen Umgebung ändern Sie auch Datenobjekte, die diese Bindungen verwenden. Zum Beispiel:
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
Beachten Sie, dass sich nach der Neudefinition der Bindung für x
auch die Funktion f
geändert hat, da ihr Körper diese Bindung verwendet.
Vergleichen Sie dies mit Sprachen, in denen die Neudefinition einer Variablen die alte Bindung nicht löscht, sondern nur schattiert , dh sie in dem Bereich unsichtbar macht, der nach der neuen Definition kommt. Sehen Sie, was passiert, wenn Sie denselben Code in die SML REPL schreiben:
- 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
Beachten Sie, dass x
die Funktion nach der zweiten Definition von f
immer noch die Bindung verwendet x = 1
, die zum Zeitpunkt der Definition im Gültigkeitsbereich war, dh die Bindung val x = 100
überschreibt die vorherige Bindung nicht val x = 1
.
Fazit: Mit Clojure können Sie die globale Umgebung mutieren und Bindungen darin neu definieren. Es wäre möglich, dies zu vermeiden, wie es andere Sprachen wie SML tun, aber das def
Konstrukt in Clojure soll auf eine globale Umgebung zugreifen und diese mutieren. In der Praxis ist dies sehr ähnlich zu dem, was die Zuweisung in imperativen Sprachen wie Java, C ++, Python tun kann.
Trotzdem bietet Clojure viele Konstrukte und Bibliotheken, die Mutationen vermeiden, und Sie können einen langen Weg zurücklegen, ohne sie überhaupt zu verwenden. Das Vermeiden von Mutationen ist bei weitem der bevorzugte Programmierstil in Clojure.
Bei Clojure geht es darum , den veränderlichen Zustand durch Steuern der Mutationspunkte (dh
Ref
s,Atom
s,Agent
s undVar
s) zu verwalten. Natürlich kann jeder Java-Code, den Sie über Interop verwenden, tun, was ihm gefällt.Wenn Sie a
Var
(im Gegensatz zu z. B. einer lokalen Variablen) an einen anderen Wert binden möchten, dann ja. Wie in Vars und der globalen Umgebung erwähnt , werdenVar
s speziell als einer der vier "Referenztypen" von Clojure aufgenommen (obwohl ich sagen würde, dass sie sich dort hauptsächlich auf dynamischeVar
s beziehen ).Mit Lisps gibt es eine lange Geschichte der Durchführung interaktiver, explorativer Programmieraktivitäten über die REPL. Dies beinhaltet häufig das Definieren neuer Variablen und Funktionen sowie das Neudefinieren alter Variablen und Funktionen. Außerhalb der REPL wird das Zurücksetzen von
def
aVar
jedoch als schlechte Form angesehen.quelle
Von Clojure für die Tapferen und Wahren
quelle