What's wrong with ActivePattern matching against System.Type?

100 views Asked by At
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)

2

There are 2 answers

3
Fyodor Soikin On BEST ANSWER

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.

0
scrwtp On

To add to Fyodor's answer, the spec is very explicit in outlining valid active pattern forms (more so than MSDN at least) - see paragraph 7.2.3 Active Patterns for details.

The five valid forms are:

  • Single case - (|CaseName|) inp
  • Partial - (|CaseName|_|) inp
  • Multi-case - (|CaseName1|...|CaseNameN|) inp
  • Single case with parameters - (|CaseName|) arg1 ... argn inp
  • Partial with parameters - (|CaseName|_|) arg1 ... argn inp

Other active pattern functions are not permitted.

What's most relevant here, there's no way to combine a multi-case pattern with additional parameters.

Related Questions in F#