Можно ли установить значения по умолчанию для типов Дискриминационный Союз?
Я реализовал тип "Дискриминационный союз", который будет использоваться для выбора функции:
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 вместо этого.