单值歧视工会?

Single value discriminated unions?

在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 且无法更改。

此代码将完成您想要的:

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 风格的多态性。 有区别的并集不是表达求和类型的唯一方式,只是最好的。但是,抽象 类 也可以起到同样的作用。

证明:此答案中描述的代码。

正如@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 }  // (*)

(*) 如果没有 BadDog 类型定义,您可以使用 JackRussell 而不是 Breed.JackRussell(解决歧义)。

您在评论中提到希望以更直接的方式匹配狗的名字。考虑这个 active pattern:

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

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