To flatten the nested match
constructs, you'll need to use nested patterns. This works best for discriminated unions (as pointed out by Brian - and I agree that designing F# code to use primarily discriminated unions is the best thing you can do).
Otherwise, you'll need some active patterns if you want to write the code succinctly using match
(ssp posted one example, which shows active patterns specifically for your problem). However, you can do this using the following two reusable active patterns:
let (|TryCast|_|) a : 'res option =
match (box a) with
| :? 'res as r -> Some(r)
| _ -> None
let (|Value|) (l:Lazy<_>) = l.Value
The first one is like :?
, but it allows you to nest other patterns to match the value (which isn't possible with as
). The second one forces evaluation of lazy value (I suppose that both of them could be declared in F# libraries as they are quite useful). Now you can write:
match lazy cond.EvalBool(), lazy body.Eval() with
| Value(true), Value(TryCast((Break(scope) : ControlFlowModifier)) as e) ->
e :> obj //Break is a DU element of ControlFlowModifier
| Value(true), _ ->
next() //all other values should call next()
| _, _ -> null
EDIT: As Roger pointed out in a comment, this version of the code may not be very readable. I think a better option would be to use only TryCast
and format your original code slightly differently (although this isn't completely standard indentation, it is correct and F# compiler handles it fine):
match cond.EvalBool() with
| false -> null
| true ->
match body.Eval() with
| TryCast(Break(scope) as e) -> e :> obj
| _ -> next()
This is probably the most readable option based on pattern matching, but you could also use if
instad of the first match
as in the version by kvb and combine it with TryCast
(this really depends on personal preferences):
if cond.EvalBool() then
match body.Eval() with
| TryCast(Break(scope) as e) -> e :> obj
| _ -> next()
else null
In any case, I believe that TryCast
makes the code more readable as you avoid one nesting (which is othervise required because of :? .. as ..
).