Чтобы ответить на ваш вопрос об активных шаблонах, позвольте мне привести более простой пример:
let (|Odd|Even|) n =
if n % 2 = 0 then Even else Odd
Когда вы объявляете шаблон с несколькими опциями, используя (|Odd|Even|)
, компилятор понимает его как функцию, которая возвращает значение типа Choice<unit, unit>
. Таким образом, активным шаблоном, с которым вы можете работать, является целая комбинация |Odd|Even|
, а не просто две конструкции, которые вы можете использовать независимо (например, |Odd|
и |Even|
).
Можно рассматривать активные шаблоны как функции первого класса, но если вы используете шаблоны с несколькими опциями, вы не сможете сделать с этим много:
let pattern = (| Odd | Even |) ;;
Шаблон val: int -> Choice
Вы можете написать функцию, которая проверяет, соответствует ли значение указанному шаблону, но вам нужно много функций (потому что существует множество типов Choice
, перегруженных числом параметров типа):
let is1Of2 pattern item =
match pattern item with
| Choice1Of2 _ -> true
| _ -> false
> is1Of2 (|Odd|Even|) 1
val it : true
Нечто подобное будет работать в вашем случае, но оно далеко от совершенства.
Вы можете сделать немного лучше, если объявите несколько частичных активных шаблонов (но тогда вы, конечно, потеряете некоторые приятные аспекты полностью активных шаблонов, такие как проверка полноты):
let (|Odd|_|) n =
if n % 2 = 0 then None else Some()
let (|Even|_|) n =
if n % 2 = 0 then Some() else None
Теперь вы можете написать функцию, которая проверяет, соответствует ли значение шаблону:
let matches pattern value =
match pattern value with
| Some _ -> true
| None -> false
> matches (|Odd|_|) 1;;
val it : bool = true
> matches (|Even|_|) 2;;
val it : bool = true
Резюме Хотя может быть более или менее элегантный способ добиться того, что вам нужно, я бы, наверное, подумал, дают ли активные шаблоны какое-то большое преимущество перед использованием стандартных функций. Может быть, лучше сначала улучшить код, используя функции, а затем решить, какая из конструкций будет полезна в качестве активных шаблонов, а затем добавить активные шаблоны. В этом случае обычный код не будет выглядеть намного хуже:
type LegResult = LegIsSettled | LegIsConfirmed | LegIsUnsettled
let getLegStatus (l: tradeLeg) =
if Helper.exists l.actuals then LegIsSettled
elif Helper.exists l.confirmedPrice then LegIsConfirmed
else LegIsUnsettled
// Later in the code you would use pattern matching
match getLegStatus trade with
| LegIsSettled -> // ...
| LegIsUnSettled -> // ...
// But you can still use higher-order functions too
trades |> List.exist (fun t -> getLegStatus t = LegIsSettled)
// Which can be rewritten (if you like point-free style):
trades |> List.exist (getLegStatus >> ((=) LegIsSettled))
// Or you can write helper function (which is more readable):
let legStatusIs check trade = getLegStatus trade = check
trades |> List.exist (legStatusIs LegIsSettled)