Извините, нет
Извините, но это невозможно.Взгляните на https://fsharpforfunandprofit.com/posts/fsharp-decompiled/#unions - вы увидите, что F # компилирует разграниченные объединения в классы .NET, каждый из которых отделен друг от друга без общих предков (конечно, кроме Object
).Компилятор не пытается идентифицировать подмножества или надмножества между различными DU.Если бы это работало так, как вы предлагали, это было бы серьезным изменением, потому что единственный способ сделать это - сделать подмножество DU базовым классом, а класс надмножества - его производным классом с дополнительным свойством.И это приведет к следующему поведению изменения кода:
type PhoneNumber =
| Valid of string
| Invalid
type EmailAddress =
| Valid of string
| ValidButOutdated of string
| Invalid
let identifyContactInfo (info : obj) =
// This came from external code we don't control, but it should be contact info
match (unbox obj) with
| :? PhoneNumber as phone -> // Do something
| :? EmailAddress as email -> // Do something
Да, это плохой код, и его следует писать по-другому, но это иллюстрирует суть.При текущем поведении компилятора, если identifyContactInfo
получит объект EmailAddress
, тест :? PhoneNumber
не будет выполнен, и он войдет во вторую ветвь совпадения и будет рассматривать этот объект (правильно) как адрес электронной почты.Если бы компилятор угадывал надмножества / подмножества на основе имен DU, как вы предлагаете здесь, то PhoneNumber
будет считаться подмножеством EmailAddress
и, следовательно, станет его базовым классом.И затем, когда эта функция получит объект EmailAddress
, тест :? PhoneNumber
будет успешным (поскольку экземпляр производного класса всегда может быть приведен к типу его базового класса).И тогда код будет вводить ветку first выражения соответствия, и ваш код может затем попытаться отправить текстовое сообщение на адрес электронной почты.
Но подождите ...
То, что вы пытаетесь сделать, может быть достигнуто путем вытягивания подмножеств в их собственную категорию DU:
type AorB =
| A of int
| B of string
type ABC =
| AorB of AorB
| C of decimal
type ABD =
| AorB of AorB
| D of bool
Тогда ваши выражения соответствия для ABC
могут выглядеть следующим образом:
match foo with
| AorB (A num) -> printfn "%d" num
| AorB (B s) -> printfn "%s" s
| C num -> printfn "%M" num
И если вам нужно передать данные между ABC
и ABD
:
let (bar : ABD option) =
match foo with
| AorB data -> Some (AorB data)
| C _ -> None
Это не большая экономия, если в вашем подмножестве есть только два общих случая.Но если ваше подмножество составляет дюжину случаев или около того, возможность передавать эти дюжины как единое целое делает этот дизайн привлекательным.