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

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

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

2 ответа

Решение

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

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

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

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

Общее правило для разрешения конфликтов имен в F# - "последнее объявление выигрывает". Потому что ваш пользовательский DU объявлен после Option, его конструкторы Some а также None победить тех из 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

Если вы проверяете типы 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 вместо этого.

Другие вопросы по тегам