Я придумал два решения для достижения моей точной цели.Одним из них является использование активных шаблонов:
let orElse(fallback: 'a -> (unit -> 'b) option) (matcher: 'a -> (unit -> 'b) option) (arg: 'a) : (unit -> 'b) option =
let first = matcher(arg)
match first with
| Some(_) -> first
| None -> fallback(arg)
let (|StringCaseHandler|_|)(arg: obj) =
match arg with
| :? string -> Some(fun () -> "string")
| _ -> None
let (|IntCaseHandler|_|)(arg: obj) =
match arg with
| :? int -> Some(fun () -> "integer")
| _ -> None
let (|DefaultCaseHandler|_|)(arg: 'a) =
Some(fun () -> "other")
let msgHandler =
``|StringCaseHandler|_|`` |>
orElse ``|IntCaseHandler|_|`` |>
orElse ``|DefaultCaseHandler|_|``
Решение с активным шаблоном является безопасным, поскольку оно не выдает MatchFailureException
в случае отсутствия надлежащего соответствия;вместо этого возвращается None
.
Второй включает в себя определение метода расширения для функций типа 'a -> 'b
и также максимально приближен к поведению Scala для "частичной функции" orElse
, которое вызываетисключение, если результирующая функция не дает правильного соответствия:
[<Extension>]
type FunctionExtension() =
[<Extension>]
static member inline OrElse(self:'a -> 'b,fallback: 'a -> 'b) : 'a -> 'b =
fun arg ->
try
self(arg)
with
| :? MatchFailureException -> fallback(arg)
let intMatcher : obj -> string = function
| :? int -> "integer"
let stringMatcher : obj -> string = function
| :? string -> "string"
let defaultMatcher : obj -> string = function
| _ -> "other"
let msgHandler: obj -> string = intMatcher
.OrElse(stringMatcher)
.OrElse(defaultMatcher)