2009-12-09 7 views
5

Gibt es eine empfohlene Möglichkeit, mehrere Vererbungsebenen in F # zu modellieren, vermutlich mit einer diskriminierten Vereinigung?Modellierung mehrerer Vererbungsebenen mit diskriminierten Vereinigungen

Unter etwa wie folgt in C#:

class Expr { } 
class SourceExpr : Expr { } 
class JoinExpr : SourceExpr { } 
class TableExpr : SourceExpr { } 

ich dies in F # getan haben:

type SourceExpr = 
    | Join of JoinExpr 
    | Table of TableExpr 

type Expr = 
    | Source of SourceExpr 
    | ... 

Gibt es einen besseren Weg? Bietet dies das gleiche polymorphe Verhalten wie die Vererbung?

Antwort

8

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.

+0

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

+0

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. –

0

Sie können die Klassen mit Vererbung in F # modellieren, genau wie in C# ... ODER ... Sie können die Anforderungen mit diskriminierten Verbindungen modellieren, wie Sie zeigen.

Nein, diskriminierte Vereinigungen erlauben nicht die gleiche Art von polymorphem Verhalten, insbesondere müssen Sie möglicherweise alle Ihre Musterübereinstimmungen ändern, wenn Sie neue Fälle hinzufügen.

Es hat seine Vorteile, obwohl ich dazu tendiere, Code mit diskriminierten Gewerkschaften in F # zu schreiben, und Vererbung in C# ... UNLESS .... Ich habe eine spezielle Sorge über die Erweiterbarkeit.

modellieren Sie keine Ebenen der Vererbung mit diskriminierten Gewerkschaften ... Sie vergleichen Äpfel mit Karotten.

Sie können die gleichen Anforderungen in beiden Paradigmen modellieren ... Ich würde in Ihrem Kontext diskriminierte Union gehen .... Ich denke, es wird den Code einfacher zu lesen machen .... aber das ist subjektiv.

Verwandte Themen