Ich möchte auf einer niedrigen Ebene verstehen, was passieren würde, wenn die Datenstruktur nicht persistent ist.
Betrachten wir einen Pseudozufallszahlengenerator mit einem riesigen Zustandsraum (wie " Mersenne Twister " mit einem Zustand von 2450 Bytes) als Datenstruktur. Wir wollen wirklich keine Zufallszahl mehr als einmal verwenden, daher scheint es wenig Grund zu geben, diese als unveränderliche persistente Datenstruktur zu implementieren. Fragen wir uns nun, was im folgenden Code schief gehen könnte:
mt_gen = CreateMersenneTwisterPRNGen(seed)
integral = MonteCarloIntegral_Bulk(mt_gen) + MonteCarloIntegral_Boundary(mt_gen)
Die meisten Programmiersprachen geben nicht die Reihenfolge an, in der MonteCarloIntegral_Bulk
und MonteCarloIntegral_Boundary
ausgewertet werden soll. Wenn beide einen Verweis auf ein veränderbares mt_gen als Argument verwenden, kann das Ergebnis dieser Berechnung plattformabhängig sein. Schlimmer noch, es könnte Plattformen geben, auf denen das Ergebnis zwischen verschiedenen Läufen überhaupt nicht reproduzierbar ist.
Man kann eine effiziente veränderbare Datenstruktur für mt_gen entwerfen, so dass jede Verschachtelung der Ausführung von MonteCarloIntegral_Bulk
und MonteCarloIntegral_Boundary
"ein korrektes" Ergebnis ergibt, eine andere Verschachtelung jedoch im Allgemeinen zu einem anderen "korrekten" Ergebnis führt. Diese Nichtreproduzierbarkeit macht die entsprechende Funktion "unrein" und führt auch zu einigen anderen Problemen.
Die Nichtreproduzierbarkeit kann vermieden werden, indem eine feste sequentielle Ausführungsreihenfolge erzwungen wird. In diesem Fall kann der Code jedoch so angeordnet werden, dass immer nur ein einziger Verweis auf mt_gen verfügbar ist. In einer typisierten funktionalen Programmiersprache könnten Eindeutigkeitstypen verwendet werden, um diese Einschränkung zu erzwingen, wodurch sichere veränderbare Aktualisierungen auch im Kontext von rein funktionalen Programmiersprachen ermöglicht werden. Das alles klingt vielleicht nett und gut, aber zumindest theoretisch sind Monte-Carlo-Simulationen peinlich parallel, und unsere "Lösung" hat gerade dieses Eigentum zerstört. Dies ist nicht nur ein theoretisches Problem, sondern ein sehr reales praktisches Problem. Wir müssen jedoch die Funktionalität unseres Pseudozufallszahlengenerators und die Folge der von ihm erzeugten Zufallszahlen ändern, und keine Programmiersprache kann dies automatisch für uns tun. (Natürlich können wir auch eine andere Pseudozufallszahlenbibliothek verwenden, die bereits die erforderliche Funktionalität bietet.)
Auf einer niedrigen Ebene führen veränderbare Datenstrukturen leicht zu einer Nichtreproduzierbarkeit (und damit zu Verunreinigungen), wenn die Ausführungsreihenfolge nicht sequentiell und fest ist. Eine typische zwingende Strategie, um mit diesen Problemen umzugehen, besteht darin, sequentielle Phasen mit fester Ausführungsreihenfolge zu haben, in denen sich veränderbare Datenstrukturen ändern, und parallele Phasen mit willkürlicher Ausführungsreihenfolge, in denen alle gemeinsam genutzten veränderlichen Datenstrukturen konstant bleiben.
Andrej Bauer sprach das Aliasing für veränderbare Datenstrukturen an. Interessanterweise haben verschiedene imperative Sprachen wie Fortran und C unterschiedliche Annahmen über das erlaubte Aliasing von Funktionsargumenten, und die meisten Programmierer sind sich überhaupt nicht bewusst, dass ihre Sprache ein Aliasing-Modell hat.
Unveränderlichkeit und Wertsemantik werden möglicherweise leicht überbewertet. Wichtiger ist, dass das Typsystem und das logische Framework (wie das abstrakte Maschinenmodell, das Aliasing-Modell, das Concurrency-Modell oder das Speicherverwaltungsmodell) Ihrer Programmiersprache eine ausreichende Unterstützung für das "sichere" Arbeiten mit "effizienten" Daten bieten Strukturen. Die Einführung der "Verschiebungssemantik" in C ++ 11 mag aus theoretischer Sicht einen großen Rückschritt in Bezug auf Reinheit und "Sicherheit" bedeuten, in der Praxis ist es jedoch umgekehrt. Das Typensystem und der logische Rahmen der Sprache wurden erweitert, um große Teile der mit der neuen Semantik verbundenen Gefahr zu beseitigen. (Und selbst wenn raue Kanten bleiben, heißt das nicht, dass dies nicht durch ein "besseres"
Uday Reddy sprach das Problem an, dass Mathematik niemals mit veränderlichen Datenobjekten funktioniert und dass die Typsysteme für Funktionsprogramme für unveränderliche Datenobjekte gut entwickelt sind. Dies erinnerte mich an Jean-Yves Girards Erklärung, dass Mathematik nicht für die Arbeit mit veränderlichen Objekten verwendet wird, wenn er versucht, lineare Logik zu motivieren.
Man könnte sich fragen, wie man das Typsystem und den logischen Rahmen von funktionalen Programmiersprachen erweitern kann, um "sicher" mit "effizienten" wandelbaren nicht persistenten Datenstrukturen arbeiten zu können. Ein Problem könnte sein, dass klassische Logik und Boolesche Algebren möglicherweise nicht das beste logische Framework für die Arbeit mit veränderlichen Datenstrukturen sind. Vielleicht sind lineare Logik und kommutative Monoide für diese Aufgabe besser geeignet? Vielleicht sollte ich lesen, was Philip Wadler über lineare Logik als Typsystem für funktionale Programmiersprachen zu sagen hat ? Aber selbst wenn die lineare Logik dieses Problem nicht lösen kann, heißt das nicht, dass das Typsystem und der logische Rahmen einer funktionalen Programmiersprache nicht erweitert werden können, um "sicher" und "effizient" zu sein.