Я применил тип дискриминированного соединения, который будет использоваться для выбора функции:
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
Вы можете пометить свой DU атрибутом [<RequireQualifiedAccess>]
.
Это означает, что вам нужно будет квалифицировать имя дела с типом всякий раз, когда вы используете его в коде - что вы сейчас делаете в любом случае в вашем match
выражении.
Таким образом, неквалифицированный Some
все равно будет разрешен как Option.Some
, несмотря на то, что вы повторно используете имя.
Полезно знать, когда вы хотите использовать мгновенное имя для случая DU - например None
, Yes
, Failure
и т. д. - это само по себе было бы двусмысленным или запутанным читатель (или компилятор, если на то пошло).
Общее правило для разрешения конфликтов имен в 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.