2017-08-17 65 views
2

F#中是否可能有一個區分的聯合,其值只能被賦值一次?我想像的是這樣的:單值歧視聯盟?

type DogAttributes = { Age: int; Color: string; } 

type Dog = 
| Rex of DogAttributes ({ Age = 5; Color = "Brown"; } 
| Fido of DogAttributes ({ Age = 3; Color = "White"; } 

Rex值將始終具有指定的DogAttributes並且無法更改。

+2

不,這是不可能的。我不確定如何使用它。 – TeaDrivenDev

+1

如果我在編譯時知道我想要使用的特定狗的所有值,這將非常有用。顯然,我可以在其他地方保存這些數據,然後查看它,這正是我現在正在做的。 –

+3

'Rex'和'Fido'不喜歡*類型*,他們覺得*值*。這似乎是一個更好的方式來模擬你的問題域是'let rex = DogAttributes {Age = 5;顏色=「布朗」}'。 – rmunn

回答

2

正如@rmunn指出的那樣,您似乎將類型與值混淆在一起。 RexFido應該是同一實體的實例。

type Dog = { Name: string; Age: int; Color: string } 

識別聯合不到位這裏要麼,它們可以被認爲是枚舉有益處。

type Breed = 
| JackRussell 
| Labrador 
| Poodle 

當然,你可以結合他們...

type BadDog = 
| JackRussell of Dog 
| Labrador of Dog 
| Poodle of Dog 

let badJack = JackRussell({ Name = "Rex" ; Age = 5 ; Color = "brown" }) 

let badName = 
    match badJack with 
    | JackRussell(dog) 
    | Labrador(dog) 
    | Poodle(dog) 
     -> dog.Name 

...但在給定的環境下,你會做更多的匹配則不可取。

type GoodDog = { Name: string; Age: int; Color: string; Breed: Breed } 
let goodJack = { Name = "Rex" ; Age = 5 ; Color = "brown" ; Breed = Breed.JackRussell } // (*) 

(*)如果沒有,你可以用過JackRussell代替Breed.JackRussell(解決歧義)的BadDog類型定義。

在你提到的評論中,想要以更直接的方式與狗的名字相匹配。考慮這個active pattern

let (|DogName|) = function dog -> dog.Name 

match goodJack with 
| DogName "Rex" 
    -> printfn "Hello Rex" 
| _ -> printfn "Hello world" 
+1

顯示活動模式解決方案的好主意。 – rmunn

0

此代碼將完成你想要的:

type DogAttribute = { Age : int; Color : string } 

type Dog = 
| Rex of DogAttribute 
| Fido of DogAttribute 

[<AbstractClass>] 
type DogBase(dog, age, color) = 
    member x.Age = age 
    member x.Color = color 
    member x.Dog = dog { Age = x.Age; Color = x.Color}  

type Rex() = 
    inherit DogBase(Dog.Rex, 5, "Brown")   

type Fido() = 
    inherit DogBase(Dog.Fido, 3, "White") 

但是,在這一點上,它好像你會更好只使用OO風格多態性。 歧視的工會不是唯一的方式來表達總和類型,只是最好的。但是,抽象類也可以發揮作用。

證明:此答案中描述的代碼。