2010-07-07 3 views
6

ich so etwas schreiben wollen:Gemeinsame Fälle in F # diskriminiert Gewerkschaften

type NumExp = Num of float 

type Exp = 
    | Num of float 
    | Dot of NumExp * NumExp 
    | Op of string * Exp * Exp 

let getValue (Num(n) : NumExp) = n 

Der Compiler beschwert sich über einen Konflikt zwischen NumExp und Exp in getValue. Auch die folgenden fehlschlägt:

let getValue (nn : NumExp) = match nn with | Num(n) -> n 

Gibt es eine Möglichkeit, den gleichen Fall in beiden diskriminiert Gewerkschaften zu verwenden, die mit Funktionen arbeitet? Die DU-Definitionen selbst sind in Ordnung.

Ich möchte den gleichen Fall verwenden, um eine Dereferenzierungsebene wie

type Exp = 
    | NumExpExp of NumExp 
    | Dot of NumExp * NumExp 
    | Op of string * Exp * Exp 

in der Exp Definition zu vermeiden hinzufügen. Ich fühle mich hier etwas sehr Grundlegendes vermisst.

Der Grund, warum ich NumExp habe ist, dass ich zu ‚plug‘ 2 Exp s in eine Dot (statt 2 Schwimmer) in der Lage sein wollen, weil es Ausdrücke einfacher Generierung macht, aber sie können nicht Exp, nur numerisch sein .

EDIT: was ich wollte wirklich wissen, ist, ob die beiden Fälle in den beiden DUs als die gleiche Einheit behandelt werden (Art wie Exp „einschließlich“ NumExp). Ich realisiere jetzt Exp.Num und NumExp.Num sind völlig separate Einheiten. Tomas bietet eine gute Möglichkeit, die beiden folgenden Fälle zu unterscheiden.

Antwort

13

Wenn Sie zwei diskriminierte Gewerkschaften mit widersprüchlichen Namen der Fälle haben, können Sie sich voll qualifizierte Namen des diskriminierten Vereinigung Falles verwenden:

let getValue (NumExp.Num(n)) = n 

Ein vollständigeres Beispiel würde wie folgt aussehen:

let rec eval = function 
    | Exp.Num(f) -> f 
    | Exp.Dot(NumExp.Num(f1), NumExp.Num(f2)) -> 
     // whatever 'dot' represents 
    | Exp.Op(op, e1, e2) -> 
     // operator 

Dies verwendet immer vollständig qualifizierte Namen, was wahrscheinlich eine gute Idee ist, wenn die Namen einfach genug sind und es widersprüchliche Fälle gibt (was zu einer Verwirrung führen könnte).

EDIT: In Bezug auf den Austausch von Fällen - es gibt keine automatische Art und Weise, dies zu tun, aber man konnte einen Fall, in Ihrem Exp haben, die einfach Werte von NumExp enthält. Zum Beispiel wie folgt aus:

type NumExp = 
    | Num of float 

type Exp = 
    // first occurrence of NumExp is just a name, but F# allows us to reuse 
    // the name of the type, so we do that (you could use other name) 
    | NumExp of NumExp 
    // other cases 

Wenn eval Funktion schreiben würden Sie dann schreiben (beachten Sie, dass wir nicht mehr das Problem mit Namenskonflikte haben, so dass wir nicht voll qualifizierte Namen müssen):

| NumExp(Num f) -> f 
| Op(op, e1, e2) -> // ... 
+0

Dank Tomas, die Disambiguierung wirkt Wunder. Was ich jedoch fragte, war mehr wie "Können die beiden Fälle in den beiden DUs als dieselbe Entität behandelt werden?", Oder mit anderen Worten, "Can Exp" kann "NumExp?" Enthalten. Aber von deiner Antwort ist die Antwort nein. Vielen Dank! – Mau

+2

@Mau: Ich habe einige Informationen über die Aufteilung der Fälle zwischen verschiedenen diskriminierten Gewerkschaften hinzugefügt. Es ist nicht möglich, aber Sie können eine in die andere einschließen. –

+0

danke, genau das habe ich gemacht :-) – Mau

-1

Nur eine Bemerkung: Warum brauchen Sie die so gebauten Gewerkschaften?

Ich würde eine von zwei Optionen gewählt haben:

type NumExp = Num of float 

type Exp = 
    | Num of float 
    | Dot of float * float 
    | Op of string * Exp * Exp 

, die die mehr einfach ist, oder

type NumExp = Num of float 

type Exp = 
    | NumExp 
    | Dot of float * float 
    | Op of string * Exp * Exp 

In diesem zweiten Fall Ihre Funktion

let getValue (Num(n) : NumExp) = n 

arbeitet als Sie haben jetzt eine Definition von NumExp.

+0

Danke Muhammad. Die erste Lösung ist in der obigen Frage ausgeschlossen (schwieriger im Rest des Codes zu behandeln). Die zweite Lösung ist syntaktisch korrekt und semantisch was ich möchte, aber erzeugt eine andere Datenstruktur als ich brauche ('Exp.NumExp' ist nur ein leerer Fall, kein Feld vom Typ' NumExp'). – Mau

2

Wenn das möglich ist (z. B. mit polymorphen Varianten in OCaml), können Sie eine Menge damit machen, aber (leider) hat F # diese Sprachfunktion nicht, so dass es derzeit nicht in der Lage ist, die gewünschten Unionstypen auszudrücken. Sie könnten jedoch in Betracht ziehen, stattdessen OOP zu verwenden ...

2

Sie können interfaces as a substitute verwenden. Das bringt ein wenig syntaktischen Overhead, aber das ist der beste Weg, den ich gefunden habe, dies zu tun.

type IExp = interface end 

type NumExp = 
     | Num of float 
     interface IExp 
type Exp = 
     | Dot of NumExp * NumExp 
     | Op of string * IExp * IExp 
     interface IExp 

// This function accepts both NumExp and Exp 
let f (x:IExp) = match x with 
    | :? NumExp as e -> match e with 
     | Num v -> "Num" 
    | :? Exp as e -> match e with 
     | Dot (e1,e2) -> "Dot" 
     | Op (op,e1,e2) -> "Op" 
    | _ -> invalidArg "x" "Unsupported expression type" 

// This function accepts only NumExp 
let g = function 
    | Num v -> "Num" 
+1

Uhmm Ich bin mir nicht sicher, ob ich folge. Wie löst dies das Problem des * Verwendens des 'Num'-Literals in beiden Vereinigungen *? – Mau

+1

Sie können 'IExp' verwenden, wenn Sie beide DUs (3 kombinierte Fälle) und' NumExp' verwenden möchten, wenn Sie nur 'Num' verwenden möchten. Die 'f'-Funktion in einem Beispiel einer Funktion, die alle 3 Fälle verwendet/akzeptiert. Im Wesentlichen haben Sie "IExp" das DU mit 3 Fällen gemacht, und "Exp" ist nur da im Hintergrund. – dtech

+1

Ich sehe! Tatsächlich nett! – Mau