3

我实现了一个可区分的联合类型,用于选择一个函数:

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

这对我来说看起来不错,但编译器开始查看属于这个 DU 的所有实例,而不是SomeDU 。 NoneOption

我不想SomeOption.SomeNone替换我的所有代码Option.None

有没有办法告诉编译器不合格SomeNone实际上是Option.SomeOption.None

或者我应该给这些 DU 案例起不同的名字,比如AtLeastOneExactlyZero

4

2 回答 2

6

在 F# 中解决名称冲突的一般规则是“最后声明获胜”。因为你的自定义 DU 是在声明之后,Option它的构造函数Some和.NoneOption

但是这条规则提供了一种解决问题的方法:您只需要在自定义 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

如果您检查上述代码中的gxf和的类型:y

> g
g : Bogus -> int

> f
f : 'a option -> int

> x
Bogus

> y
int option

函数g和值x被推断为分别具有类型Bogus -> intand Bogus,因为SomeandNone在它们的主体中引用Bogus.Someand Bogus.None

函数f和值y被推断为具有Option-related 类型,因为SomeNone在它们的主体中指的是我刚刚在上面定义的Some函数和活动模式。(|Some|None|)

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

于 2018-07-14T01:56:44.520 回答
3

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

这意味着无论何时在代码中使用案例名称时,都需要使用类型来限定案例名称 - 这是您现在在match表达式中所做的事情。

这样,尽管您重用了名称,但unqualifiedSome仍将被解析为 mean 。Option.Some

这是一种有用的技术,可以知道您何时想为 DU 案例使用一个时髦的名称 - 例如None,YesFailure- 这本身会使读者(或编译器,就此而言)模棱两可或感到困惑。

于 2018-07-14T12:41:38.907 回答