Es ist schwer, hier ohne weitere Informationen zu präskriptiv zu sein. Je nachdem, was Sie zu tun versuchen, kann es sinnvoller sein, eine Klassenhierarchie oder eine diskriminierte Union (DU) zu verwenden. Die häufigsten/offensichtlichen Kompromisse sind, dass Klassenhierarchien "offen" sind, während DUs "geschlossen" sind. Das heißt, Sie können einfach neue Typen zu einer Klassenhierarchie hinzufügen, aber das Hinzufügen einer neuen Operation (abstrakte Methode für die Basisklasse) erfordert das Ändern aller vorhandenen Klassen. Im Gegensatz dazu können Sie mit DUs einfach eine neue Operation hinzufügen (Funktion, die dem Datentyp entspricht), aber um einen neuen Fall (Unterklasse) hinzuzufügen, müssen Sie den Typ neu definieren und alle vorhandenen Operationen aktualisieren, um den neuen Fall zu bearbeiten . (Dies wird manchmal als "das Ausdrucksproblem" bezeichnet.)
Ein typisches Beispiel, das für DUs gut ist, ist ein Compiler; Sie haben einen abstrakten Syntaxbaum für Sprachen, in dem die Sprache und die Baumstruktur festgelegt sind. Sie können jedoch viele verschiedene Baumumwandlungsoperationen im Compiler erstellen. Ein typisches Beispiel für Klassenhierarchien sind UI-Frameworks. Sie haben einige Basisklassen, die alle Operationen definieren, die Widgets bereitstellen müssen (Zeichnen, Größe ändern, ...), aber dann fügen Benutzer ihre eigenen benutzerdefinierten Subtypen mit zusätzlichen Funktionen hinzu.
Ich arbeite an einem Parser, also denke ich, DUs sind die richtige Wahl. Aber es gibt Ausdrücke, die von anderen zu erben scheinen. Es gibt Funktionen, die ein Join oder eine Tabelle akzeptieren sollten, die beide Quellen sind. Es gibt auch Funktionen, die einen Expr akzeptieren sollten, von dem Source eine der vielen Optionen ist. Ich kann nicht anders, als in OO-Begriffen zu denken, aber ich frage mich, ob es eine bessere Möglichkeit gibt, diese relativ statische Hierarchie zu modellieren, während die gleiche Art von polymorphem Verhalten beibehalten wird. – Daniel
Die Modellierung mit Typen funktioniert gut. Wenn Sie zwei Typen haben, die "auch" die gleiche Sache sind, fügen Sie einen neuen Typ hinzu, der zwischen ihnen unterscheidet und diesen verwendet. Der einzige Nachteil ist, dass Sie mehr Dekonstruktion durchführen müssen, aber Pattern Matching und aktive Patterns, wo Sie sie brauchen, machen das ziemlich erträglich. Ich mache das mit einigen ziemlich komplizierten rekursiven Typen und einem FParsec Parser und es funktioniert ziemlich gut. –