2010-07-07 7 views
6

chcę napisać coś takiego:Udostępnione przypadki w F # dyskryminowane związków

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 

Kompilator narzeka konfliktu między NumExp i Exp w getValue. Nawet nie następuje:

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

Czy istnieje sposób, aby korzystać z tej samej sprawy w obu dyskryminowanych związków, które współpracuje z funkcji? Definicje dalszych użytkowników są w porządku.

chcę używać tej samej sprawy, aby uniknąć dodawania poziom zadnie jak

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

w definicji Exp. Czuję, że brakuje tu czegoś bardzo prostego.

Powodem Mam NumExp jest, że chcę, aby móc „wtyczki” 2 Exp S w Dot (zamiast 2 pływaki), ponieważ to sprawia, że ​​generowanie wyrażenia łatwiejsze, ale nie mogą one być dowolny Exp tylko numeryczna .

EDIT: co naprawdę chciałem wiedzieć, czy te dwa przypadki w dwóch dalszych użytkowników mogą być traktowane jako ten sam podmiot (niby jak Exp „w tym” NumExp). Zdaję sobie teraz sprawę, że Exp.Num i NumExp.Num są całkowicie oddzielnymi jednostkami. Tomas stanowi dobry sposób na rozróżnienie dwóch przypadków poniżej.

Odpowiedz

13

Jeśli masz dwie dyskryminowanych związków ze sprzecznymi nazwami przypadkach można użyć pełnej nazwy z dyskryminowanej przypadku związków:

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

Pełniejszy Przykładem może wyglądać następująco:

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 

To zawsze używa w pełni kwalifikowanych nazw, co jest prawdopodobnie dobrym pomysłem, jeśli nazwy są wystarczająco proste i istnieją sprzeczne przypadki (które mogą prowadzić do nieporozumień).

EDIT: Odnośnie dzielenia przypadkach - nie ma automatyczny sposób to robić, ale można mieć sprawę w swoim Exp że po prostu zawiera wartości NumExp. Na przykład takich jak to:

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 

Pisząc eval funkcję byś następnie napisać (zauważ, że nie mamy już problemu z nazwą starć, więc nie musimy w pełni kwalifikowane nazwy):

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

Dzięki Tomas, ujednoznacznienie działa cuda. To, o co pytałem, brzmiało raczej "czy te dwie sprawy w dwóch dalszych użytkownikach można traktować jako tę samą jednostkę?", Czy też innymi słowy, "czy exp" może zawierać "NumExp?". Ale z twojej odpowiedzi odpowiedź brzmi: nie. Dzięki! – Mau

+2

@Mau: Dodałem kilka informacji o udostępnianiu spraw między różnymi dyskryminowanymi związkami. Nie jest to możliwe, ale możesz dołączyć jeden do drugiego. –

+0

dziękuję, właśnie to zrobiłem :-) – Mau

-1

Po prostu spostrzeżenie: dlaczego związki tak konstruowane są potrzebne?

bym wybrał jedną z dwóch opcji:

type NumExp = Num of float 

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

co jest bardziej proste, czyli

type NumExp = Num of float 

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

W tym drugim przypadku, czynność

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

prace jako masz teraz jedną definicję NumExp.

+0

Dzięki Muhammad. Pierwsze rozwiązanie jest wykluczone w powyższym pytaniu (trudniejsze do rozwiązania w pozostałej części kodu). Drugie rozwiązanie jest poprawne syntaktycznie i semantycznie, ale chciałbym stworzyć inną strukturę danych niż to, czego potrzebuję ('Exp.NumExp' to pusty przypadek, a nie pole typu' NumExp'). – Mau

2

Gdy jest to możliwe (np. Przy użyciu wariantów polimorficznych w OCaml), można wiele z tym zrobić, ale (niestety) F # nie ma tej funkcji językowej, więc obecnie nie jest w stanie wyrazić tego, co chcemy, używając rodzajów związków. Możesz jednak rozważyć użycie OOP zamiast ...

2

Możesz użyć interfaces as a substitute. To dodaje trochę obciążenia składniowego, ale jest najlepszym sposobem, w jaki to zrobiłem.

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 Nie jestem pewien, czy podążam. W jaki sposób rozwiązuje to problem * używania literału 'Num' w obu związkach *? – Mau

+1

Możesz użyć 'IExp', jeśli chcesz używać zarówno DU (połączone 3 przypadki), jak i' NumExp', gdy chcesz użyć tylko 'Num'. Funkcja 'f' w przykładzie funkcji, która używa/akceptuje wszystkie 3 przypadki. Zasadniczo stworzyłeś 'IExp' DU z 3 przypadkami, a' Exp' jest po prostu w tle. – dtech

+1

Rozumiem! Naprawdę miło! – Mau