可以为歧视联盟类型设置默认值吗?

时间:2018-07-14 00:10:42

标签: f# discriminated-union qualified-name

我实现了一种区分联合类型,该类型将用于选择函数:

type BooleanCombinator =
    | All
    | Some
    | None
    | AtLeast of int
    | MoreThan of int
    | NotMoreThan of int
    | LessThan of int
    | ExactlyOne
    | ExactlyTwo
    | AllButOne
    | AllButTwo

let boolToInt (b: bool) : int = if b then 1 else 0

let combineBooleans (combinator : BooleanCombinator)
                    (bools      : bool list)
                                : bool =

        let n = List.sumBy boolToInt bools

        match combinator with
        | BooleanCombinator.All -> List.forall id bools
        | BooleanCombinator.Some -> bools |> List.exists id
        | BooleanCombinator.None -> bools |> List.exists id |> not
        | BooleanCombinator.AtLeast i -> n >= i
        | BooleanCombinator.MoreThan i -> n > i
        | BooleanCombinator.NotMoreThan i -> n <= i
        | BooleanCombinator.LessThan i -> n < i
        | BooleanCombinator.ExactlyOne -> n = 1
        | BooleanCombinator.ExactlyTwo -> n = 2
        | BooleanCombinator.AllButOne -> n = bools.Length - 1
        | BooleanCombinator.AllButTwo -> n = bools.Length - 2

在我看来,这很好,但是编译器开始将SomeNone的所有实例都视为属于此DU,而不是Option DU。

我不想遍历所有代码,将Some替换为Option.Some,将None替换为Option.None

是否可以告诉编译器不合格的SomeNone实际上是Option.SomeOption.None

或者我应该为这些DU案例使用不同的名称,例如AtLeastOneExactlyZero

2 个答案:

答案 0 :(得分:6)

解决F#中名称冲突的一般规则是“最后声明获胜”。由于您的自定义DU是在Option之后声明的,因此其构造函数SomeNone胜过Option的构造函数。

但是此规则提供了一种解决问题的方法:您只需要在自定义DU之后“重新声明”声明即可。

type Bogus = Some of int | None

let g = function Some _ -> 42 | None -> 5
let x = Some 42

let inline Some a = Option.Some a
let inline None<'a> = Option.None : 'a option
let (|Some|None|) = function | Option.Some a -> Some a | Option.None -> None

let f = function Some _ -> 42 | None -> 5
let y = Some 42

如果您检查以上代码中的gxfy的类型:

> g
g : Bogus -> int

> f
f : 'a option -> int

> x
Bogus

> y
int option

推断函数g和值x分别具有类型Bogus -> intBogus,因为它们中的SomeNone请参阅Bogus.SomeBogus.None

推断函数f和值y具有与Option相关的类型,因为它们中的SomeNone引用了{{ 1}}函数和我在上面定义的Some活动模式。

当然,这是一种恢复现状的简单方法。这将使编译器信服,但是人类仍然很难阅读您的代码。我建议您改用DU的案例。

答案 1 :(得分:4)

您可以使用[<RequireQualifiedAccess>]属性标记您的DU。

这意味着您每次在代码中使用案例名称时,都必须使用类型来限定案例名称-现在您仍可以在match表达式中进行此操作。

这样,尽管您重用了名称,但不合格的Some仍将被解析为Option.Some

知道何时要为DU案例使用简明的名称是一种有用的技术,例如NoneYesFailure等-本身就是模棱两可或会使读者(或编译器)感到困惑。