Wie werden Entitäten mit einer Identität und einem veränderlichen persistenten Zustand in einer funktionalen Programmiersprache modelliert?

8

In einer Antwort auf diese Frage (geschrieben von Pete) gibt es einige Überlegungen zu OOP im Vergleich zu FP. Insbesondere wird vorgeschlagen, dass FP-Sprachen nicht sehr gut zum Modellieren von (persistenten) Objekten geeignet sind, die eine Identität und einen veränderlichen Zustand haben.

Ich habe mich gefragt, ob dies wahr ist oder mit anderen Worten, wie man Objekte in einer funktionalen Programmiersprache modellieren würde. Aufgrund meiner Grundkenntnisse in Haskell dachte ich, man könnte Monaden auf irgendeine Weise verwenden, aber ich weiß wirklich nicht genug über dieses Thema, um eine klare Antwort zu finden.

Wie werden Entitäten mit einer Identität und einem veränderlichen persistenten Zustand normalerweise in einer funktionalen Sprache modelliert?

Hier sind einige weitere Details, um zu verdeutlichen, was ich vorhabe. Nehmen Sie eine typische Java-Anwendung, in der ich (1) einen Datensatz aus einer Datenbanktabelle in ein Java-Objekt einlesen kann, (2) das Objekt auf verschiedene Arten ändern kann, (3) das geänderte Objekt in der Datenbank speichern kann.

Wie würde dies zB in Haskell umgesetzt? Ich würde den Datensatz zunächst in einen Datensatzwert einlesen (definiert durch eine Datendefinition), verschiedene Transformationen durchführen, indem ich Funktionen auf diesen Anfangswert anwende (jeder Zwischenwert ist eine neue, modifizierte Kopie des ursprünglichen Datensatzes) und dann den endgültigen Datensatzwert schreiben in die Datenbank.

Ist das alles was dazu gehört? Wie kann ich sicherstellen, dass zu jedem Zeitpunkt nur eine Kopie des Datensatzes gültig / zugänglich ist? Man möchte nicht, dass unterschiedliche unveränderliche Werte unterschiedliche Schnappschüsse desselben Objekts darstellen, um gleichzeitig zugänglich zu sein.

Giorgio
quelle
Kommt auf eine Sprache an. Rich Hickey, der Autor von Clojure, empfiehlt, dass Sie Datenstrukturen (Listen, Wörterbücher, Mengen usw.) verwenden, um Ihre Daten darzustellen. Ich bin nicht vollständig von der Idee überzeugt; Ich möchte Felder eher nach ihrem Namen als nach Index oder Mnemonik de-referenzieren und den Compiler überprüfen lassen, ob ich es nicht vermasselt habe. F # hat Strukturen, die wie Klassen sind. msdn.microsoft.com/en-us/library/dd233233.aspx Selbst wenn Sie Java oder C # verwenden, können Sie unveränderliche Objekte haben, die sich "ändern" können, indem Sie ein ganz neues Objekt erstellen.
Job
1
@gnat: Danke, dass du den Titel bearbeitet hast: Objekte war definitiv nicht der am besten geeignete Begriff.
Giorgio

Antworten:

7

Der übliche Weg, um Zustandsänderungen in einer reinen Sprache wie Haskell vorzunehmen, besteht darin, sie als Funktionen zu modellieren, die den alten Zustand annehmen und eine modifizierte Version zurückgeben. Selbst bei komplexen Objekten ist dies aufgrund der verzögerten Bewertungsstrategie von Haskell effizient. Obwohl Sie ein neues Objekt syntaktisch erstellen, wird es nicht vollständig kopiert. Jedes Feld wird nur ausgewertet, wenn es benötigt wird.

Wenn Sie mehr als ein paar lokale Zustandsänderungen haben, können die Dinge ungeschickt werden. Hier kommen Monaden ins Spiel. Das Monadenparadigma kann verwendet werden, um einen Zustand und seine Änderungen zu kapseln. Das Lehrbuchbeispiel ist die StateMonade, die mit einer Standardinstallation von Haskell geliefert wird. Beachten Sie jedoch, dass eine Monade nichts Besonderes ist: Es handelt sich lediglich um einen Datentyp, der zwei Methoden ( >>=und return) verfügbar macht und einige Erwartungen erfüllt (die „Monadengesetze“). Unter der Haube macht die Staatsmonade genau das Gleiche: Nehmen Sie den alten Staat und geben Sie einen modifizierten Zustand zurück; Nur die Syntax ist besser.

tdammers
quelle
2
Ich denke, Faulheit ist das falsche Wort. Es ist effizient, weil Haskell unveränderte Unterkomponenten des Staates teilen kann. Dies kann in jeder Sprache geschehen, in der die Veränderbarkeit auf Textebene erfolgt.
Daniel Gratzer
@jozefg - während das wahr sein mag, ist zumindest in Haskell seine Implementierung von Natur aus an die Faulheit der Sprache gebunden; Dies funktioniert einfach, da der gesamte Wert einer Struktur nur dann berechnet wird, wenn dies erforderlich ist, und die Thunks-Kette, die die Aktualisierungen darstellt, an diesem Punkt ausgewertet wird und nur so weit wie erforderlich, um den neuesten Wert des angeforderten Mitglieds zu ermitteln. Reine strenge Sprachen benötigen Datensatzaktualisierungsvorgänge als Grundelement, um effizient zu sein. Haskell nicht wegen seiner Faulheit.
Jules
2

Ich bin kein Entwickler von funktionalen Sprachen, also schießen Sie mich bitte in Flammen ab, wenn ich das falsch habe, aber wenn es richtig ist, könnte es eine interessante Analogie sein.

Mir wurde einmal gesagt, dass Excel im Wesentlichen eine funktionale Sprache ist. Ich spreche nicht über VBA und so weiter, ich spreche darüber, was auf dem Blatt passiert.

Sie haben also Eingaben, die tabellarisch, einzelne Zellen oder benannte Bereiche usw. sein können, und über eine Kette von Operationen erhalten Sie ein Ergebnis oder viele Ergebnisse. Ändern Sie einen Eingang und es fließt sofort durch.

Hält diese Analogie Wasser?

Ian
quelle
1
Durch das Argument der Autorität sind Sie richtig;)
phant0m
1
Ich glaube, dass die Verwendung von Excel eher der Datenflussprogrammierung ähnelt , aber es gibt einen gewissen Spielraum, in den Excel passen könnte. Persönlich bin ich mir nicht sicher, ob ich es überhaupt eine Sprache nennen würde ...
Izkata
1
Da hast du recht. Dennoch ist Excel in dieser Hinsicht funktionsfähig. PS: Ich empfehle Ihnen, sich die Präsentation anzusehen, ich finde sie ziemlich interessant.
Phant0m
@ Izkata Excel würde einen Sonderfall der Datenflussprogrammierung darstellen: reaktive Programmierung
itsbruce
2

Ich gehe davon aus, dass Sie über die staatenlose Eigenschaft einer reinen funktionalen Sprache sprechen.

Sie nehmen den Anfangszustand als Eingabe, Sie geben den Endzustand als Ausgabe zurück. Nichts unterscheidet sich grundlegend von dem, was Sie in einer Sprache mit einem Staatsbegriff tun, außer dass Sie expliziter darüber sind und dies langwierig und weniger effizient sein kann (*).

Beachten Sie, dass Sie nicht unbedingt alle Stellen ändern müssen, die auf Ihr Objekt verweisen: Sie können ein Token enthalten, und dann müssen Sie nur die Datenstruktur ändern, die die Token ihrem aktuellen Status zuordnet. Bis dahin implementieren Sie ein staatliches System in einer zustandslosen Sprache und erhalten die Probleme staatlicher Sprachen zurück.


(*) Zum Beispiel habe ich nach der Datenstruktur gesucht und diese nicht gefunden, die es mir ermöglicht, in O (1) eine modifizierte Kopie einer Datenstruktur zurückzugeben, die auch in O (1) durch aufeinanderfolgende ganze Zahlen indizierbar ist.

Ein Programmierer
quelle
+1. Ja, ich bezog mich auf die Modellierung einer zustandsbehafteten Entität wie eines Objekts (das eine Identität und eine Geschichte nachfolgender Zustände hat) in einer zustandslosen Sprache. Was wäre das Gegenstück zu Ihrem Beispiel (markiert mit (*)) in einer Sprache wie Java? Eine Anordnung? Eine Reihe von Objekten?
Giorgio
@ Giorgio, ja. Ich weiß, wie man das in O (log n) macht, aber nicht in O (1) - und dann ist der konstante Faktor auch ein Treffer. Beachten Sie, dass ich Datenstrukturen verwendet habe, die für eine reine funktionale Sprache entwickelt wurden, um das Rückgängigmachen in Sprachen mit vollem Status zu implementieren.
AProgrammer
Im Übrigen ist ein Versuch technisch O (1) unter den gleichen vereinfachenden Annahmen, die häufig bei der Erörterung solcher Dinge verwendet werden, und ist eine Sammlung variabler Größe mit Einfüge- / Löschoperationen, deren zeitliche Komplexität nicht von der Anzahl der vorhandenen Elemente abhängt. Sie sind jedoch nicht wirklich mit einem veränderlichen Array vergleichbar.
CA McCann
Mit einer normalen Zuordnungsliste [(Int, v)]können Sie den Wert an jedem Index in konstanter Zeit ersetzen, indem Sie einfach den neuen Wert berücksichtigen. Nachteile: Der alte Wert belegt immer noch RAM und die Suche ist linear.
Singpolym
1

Kurz gesagt, jeder Status einer Entität ist eine eigene Entität. In Ihrer klassischen Geschäftslogik "Bestellen" gibt es also eine OrderEntität und eine OrderVersionEntität. Beide sind unveränderlich. Eine Logik, die eine Werbebuchung hinzufügt, verwendet die alte und eine neue Version OrderLineItemVersionals Eingabe und gibt eine neue OrderVersionEntität zurück.

Es macht einige Dinge einfacher (insbesondere "Rückgängig" -Funktionalität), aber einige Dinge sind schwieriger.

Scott Whitlock
quelle