Можно ли установить значения по умолчанию для типов дискриминационного союза?

Я применил тип дискриминированного соединения, который будет использоваться для выбора функции:

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

Это выглядело хорошо для меня, но компилятор начал смотреть на все экземпляры Some и None как принадлежащих этому DU, а не Option DU.

Я не хочу проходить через весь мой код, заменяя Some на Option.Some и None на Option.None.

Есть ли способ сообщить компилятору что неквалифицированные Some и None на самом деле Option.Some и Option.None?

Или я просто должен давать разные имена этим случаям DU, например AtLeastOne и ExactlyZero

3
задан Soldalma 14 July 2018 в 00:10
поделиться

2 ответа

Вы можете пометить свой DU атрибутом [<RequireQualifiedAccess>].

Это означает, что вам нужно будет квалифицировать имя дела с типом всякий раз, когда вы используете его в коде - что вы сейчас делаете в любом случае в вашем match выражении.

Таким образом, неквалифицированный Some все равно будет разрешен как Option.Some, несмотря на то, что вы повторно используете имя.

Полезно знать, когда вы хотите использовать мгновенное имя для случая DU - например None, Yes, Failure и т. д. - это само по себе было бы двусмысленным или запутанным читатель (или компилятор, если на то пошло).

4
ответ дан scrwtp 17 August 2018 в 12:05
поделиться

Общее правило для разрешения конфликтов имен в F # - «последнее объявление». Поскольку ваш пользовательский DU объявлен после Option, его конструкторы Some и None выигрывают от своих Option.

Но это правило предлагает способ исправить проблему: вам просто нужно «reassert» декларации после вашего пользовательского 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

Если вы проверяете типы g, x, f и y в приведенном выше коде:

> g
g : Bogus -> int

> f
f : 'a option -> int

> x
Bogus

> y
int option

Предполагалось, что функция g и значение x имеют тип Bogus -> int и Bogus соответственно, поскольку Some и None в их телах относятся к Bogus.Some и Bogus.None.

Вычислены функции f и значение y, имеющие Option -связанные типы, поскольку Some и None в их телах относятся к функции Some и (|Some|None|) активный шаблон, который я определил чуть выше.

Конечно, это довольно хакерский способ восстановить статус-кво. Это убедит компилятор, но людям все равно придется читать код. Я предлагаю вам переименовать случаи вашего DU.

6
ответ дан Fyodor Soikin 17 August 2018 в 12:05
поделиться
Другие вопросы по тегам:

Похожие вопросы: