Wenn Sie eine Klasse in einer objektorientierten Sprache definieren, werden normalerweise die Standardwerte für die Elementvariablen festgelegt. Gibt es in Haskell einen Mechanismus, um dasselbe in Datensatztypen zu tun? Und eine weitere Frage: Wenn wir nicht von Anfang an alle Werte für einen Datenkonstruktor kennen, sondern sie aus der IO-Interaktion erhalten, können wir den Typ mit etwas wie dem Builder-Muster aus OOP erstellen?
Vielen Dank im Voraus
Ein gängiges Idiom ist es, einen Standardwert zu definieren.
%Vor%Dies kann dann später (rein) mit echten Werten "aktualisiert" werden.
%Vor%Pseudocode Beispiel:
%Vor% Wenn keine sinnvollen Standardwerte vorhanden sind, können Sie die Feldwerte in Maybe
einschließen.
Wenn Sie sich abenteuerlustig fühlen, können Sie sogar einen fortgeschritteneren Ansatz verwenden, um "dazwischenliegende" Optionswerte statisch zu trennen Es können ein paar Felder fehlen, von "finalisierten", die alle Felder haben müssen. (Ich würde Haskell-Anfängern das allerdings nicht empfehlen.)
Gibt es einen Mechanismus in Haskell, um dasselbe in Datensatztypen zu machen?
Sie können den Konstruktor ausblenden und stattdessen eine Funktion als Konstruktor bereitstellen.
Sagen wir zum Beispiel, wir haben eine Liste, die wir aktualisieren wollen, zusammen mit einer Revisionsnummer, dann können wir sie wie folgt definieren:
%Vor% Nun können wir eine Funktion definieren, die BuildList
mit einer anfänglichen Liste initialisiert:
und indem wir den Konstruktor im module
Export verstecken, verbergen wir damit die Möglichkeit, ihn mit einer anderen Revision als der Revision 0
zu initialisieren. So könnte das Modul wie folgt aussehen:
so etwas wie das Builder-Muster von OOP?
Wir können zum Beispiel eine State
monad dafür verwenden. Zum Beispiel:
Also haben wir bisher get
die RevisionList
, führen Updates und put
das neue Ergebnis zurück.
Nun kann ein anderes Modul unseren Foo
importieren und den Builder wie folgt verwenden:
und jetzt können wir eine RevisionList
bei der Revision 2
mit der endgültigen Liste [1,4,2,5]
mit:
Es würde also ungefähr so aussehen:
Foo.hs
:
Bar.hs
:
Nun haben wir ein some_rev_list
mit dem "Gebäude" von some_building
:
Hier gibt es bereits gute Antworten, daher ist diese Antwort nur als Ergänzung zu den feinen Antworten von chi und Willem Van Onsem gedacht.
In gängigen objektorientierten Programmiersprachen wie Java und C # ist es nicht so, dass ein Standardobjekt nicht initialisiert ist ; Vielmehr wird ein Standardobjekt normalerweise mit Standardwerten für seine Typen initialisiert, und bei Referenztypen ist der Standardwert eine Nullreferenz.
Haskell hat keine Nullreferenzen, daher können Datensätze nicht mit Nullen initialisiert werden. Die direkteste Übersetzung von Objekten wären Datensätze, bei denen jedes einzelne Element ein Maybe
ist. Das ist jedoch nicht besonders nützlich, aber es zeigt, wie schwer es ist, Invarianten in OOP zu schützen.
Das Builder-Muster löst dieses Problem überhaupt nicht. Jeder Builder muss mit einem anfänglichen Builder-Objekt beginnen, und dieses Objekt muss ebenfalls Standardwerte haben.
Für weitere Details und viele Beispiele habe ich ein Artikelserie dazu . Die Artikelserie konzentriert sich speziell auf das Test Data Builder-Muster, aber Sie sollten in der Lage sein zu sehen, wie es im Allgemeinen auf das Fluent Builder-Muster verallgemeinert wird.
Tags und Links haskell