Erlang und offensichtlich Elixier, das darauf aufgebaut ist, umfasst Unveränderlichkeit.
Sie erlauben einfach nicht, dass sich Werte an einem bestimmten Speicherort ändern.Niemals, bis die Variable Müll gesammelt bekommt oder außerhalb des Gültigkeitsbereichs liegt.
Variablen sind nicht unveränderlich. Die Daten, auf die sie verweisen, sind unveränderlich. Aus diesem Grund wird das Ändern einer Variablen als erneutes Binden bezeichnet.
Sie richten es auf etwas anderes und ändern nicht das, worauf es zeigt.
x = 1
gefolgt von x = 2
ändert nichts an den im Computerspeicher gespeicherten Daten, bei denen die 1 eine 2 war. Es setzt eine 2 an eine neue Stelle und zeigt x
darauf.
x
ist jeweils nur für einen Prozess zugänglich, sodass dies keine Auswirkungen auf die Parallelität hat. Parallelität ist der wichtigste Ort, um sich überhaupt darum zu kümmern, ob etwas ohnehin unveränderlich ist.
Durch das erneute Binden wird der Status eines Objekts überhaupt nicht geändert. Der Wert befindet sich immer noch am selben Speicherort. Die Bezeichnung (Variable) verweist jetzt auf einen anderen Speicherort, sodass die Unveränderlichkeit erhalten bleibt. Das erneute Binden ist in Erlang nicht verfügbar, aber während es sich in Elixir befindet, bremst dies dank seiner Implementierung keine von der Erlang-VM auferlegten Einschränkungen. Die Gründe für diese Wahl werden von Josè Valim in diesem Kern gut erklärt .
Angenommen, Sie hatten eine Liste
l = [1, 2, 3]
und Sie hatten einen anderen Prozess, der Listen nahm und dann wiederholt "Sachen" gegen sie ausführte und sie während dieses Prozesses zu ändern, wäre schlecht. Sie könnten diese Liste wie senden
send(worker, {:dostuff, l})
Jetzt möchte Ihr nächster Code möglicherweise l mit mehr Werten für weitere Arbeiten aktualisieren, die nichts mit dem zu tun haben, was dieser andere Prozess tut.
l = l ++ [4, 5, 6]
Oh nein, jetzt wird dieser erste Prozess ein undefiniertes Verhalten haben, weil Sie die Liste geändert haben, oder? Falsch.
Diese ursprüngliche Liste bleibt unverändert. Was Sie wirklich getan haben, war, eine neue Liste basierend auf der alten zu erstellen und l wieder an diese neue Liste zu binden.
Der separate Prozess hat niemals Zugriff auf l. Die Daten, auf die ich ursprünglich verwiesen habe, sind unverändert, und der andere Prozess (vermutlich, sofern er nicht ignoriert wird) hat einen eigenen Verweis auf diese ursprüngliche Liste.
Was zählt, ist, dass Sie Daten nicht prozessübergreifend gemeinsam nutzen und dann ändern können, während ein anderer Prozess sie betrachtet. In einer Sprache wie Java, in der Sie einige veränderbare Typen haben (alle primitiven Typen plus Referenzen selbst), ist es möglich, eine Struktur / ein Objekt, die beispielsweise ein int enthält, gemeinsam zu nutzen und dieses int von einem Thread zu ändern, während ein anderer es liest.
Tatsächlich ist es möglich, einen großen Integer-Typ in Java teilweise zu ändern, während er von einem anderen Thread gelesen wird. Zumindest war es früher nicht sicher, ob sie diesen Aspekt der Dinge mit dem 64-Bit-Übergang eingeklemmt haben. Der Punkt ist jedenfalls, dass Sie den Teppich unter anderen Prozessen / Threads herausziehen können, indem Sie Daten an einer Stelle ändern, die beide gleichzeitig betrachten.
Das ist in Erlang und im weiteren Sinne in Elixir nicht möglich. Das bedeutet hier Unveränderlichkeit.
Um etwas genauer zu sein, in Erlang (die Originalsprache für VM Elixir läuft darauf) waren alles unveränderliche Variablen mit einfacher Zuweisung, und Elixir verbirgt ein Muster, das Erlang-Programmierer entwickelt haben, um dies zu umgehen.
Wenn in Erlang a = 3 ist, ist dies der Wert von a für die Dauer der Existenz dieser Variablen, bis sie aus dem Gültigkeitsbereich ausfällt und Müll gesammelt wird.
Dies war manchmal nützlich (nichts ändert sich nach der Zuweisung oder Musterübereinstimmung, so dass es leicht zu überlegen ist, was eine Funktion tut), aber auch etwas umständlich, wenn Sie während des Kurses, in dem eine Funktion ausgeführt wird, mehrere Dinge mit einer Variablen oder Sammlung tun.
Code würde oft so aussehen:
A=input,
A1=do_something(A),
A2=do_something_else(A1),
A3=more_of_the_same(A2)
Dies war etwas klobig und machte das Refactoring schwieriger als nötig. Elixir tut dies hinter den Kulissen, versteckt es jedoch über Makros und Code-Transformationen, die vom Compiler ausgeführt werden, vor dem Programmierer.
Tolle Diskussion hier
Unveränderlichkeit im Elixier