module Reflection =
[<RequireQualifiedAccess>]
module Type =
let isType<'a> = Unchecked.defaultof<'a>
let (|IsEqual|Isnt|) (_:'a) (t:Type):Choice<unit,unit> =
let t' = typeof<'a>
if t = t' then IsEqual else Isnt
let (|TypeOf|_|) (_:'a) (t:Type) :unit option =
if t = typeof<'a> then Some ()
else
//printfn "did not match %A to %A" typeof<'a> t
None
open Reflection
match typeof<string> with
// compiles just fine
| Type.TypeOf (Type.isType:int) as x -> Some x.Name
// does not compile
| Type.IsEqual (Type.isType:string) as x -> Some x.Name
| _ -> None
gives Type mismatch. Expecting a Type -> Choice<'a,'b> but given a Type -> 'c -> Choice<unit,unit> The type 'Choice<'a,'b>' does not match the type ''c -> Choice<unit,unit>' (using external F# compiler)
For whatever reason, patterns like this are just simply banned. Only patterns with exactly one result can accept additional parameters.
This is legal:
let (|A|) x y = if x = y then 5 else 42
let f (A "foo" a) = printfn "%A" y
f "foo" // Prints "5"
f "bar" // Prints "42"
And this is legal:
let (|B|_|) x y = if x = y then Some (y+5) else None
let f = function
| B 42 x -> printfn "%d" x
| _ -> printfn "try again"
f 42 // prints "47"
f 5 // prints "try again"
But that's it. All other active patterns must be parameterless. Both of these are illegal:
let (|A|B|) x y = ...
let (|A|B|_|) x y = ...
If I had to speculate, I would say that this has to do with predictable runtime performance. When the pattern either matches or not, the compiler can run it exactly once for every parameter value. But if the pattern returns multiple things, and some of those things are present in the match expression, and others don't, and not all of them have the same parameter - it becomes very complicated to figure out the best way to make the minimum amount of function calls.