Search code examples
f#active-pattern

F# Avoid active pattern overwriting


I have noticed I cannot create two active patterns with the same options, but I can have two with similar ones without any warning:

let (|A|B|C|) c =
   if (c = 'a') then A
   else if (c = 'b') then B
   else C

let (|A|B|D|) c =
   if (c = '1') then A
   else if (c = '2') then B
   else D

So when matching this way:

let check myvar =
  match myvar with 
    | A -> printf "match A\n"
    | n -> printf "match other %A\n" n

This happens:

check 'x' // match other 'x'  
check 'a' // match other 'a'     !!
check '1' // match A

I am a bit concerned of overwriting existing active pattern options inadvertently, for example in situations where the same word can appear in different patterns because different semantic contexts, like (|Direct|Indirect|) (route) and (|Alternating|Direct|) (current).

How can I avoid this situations?


Solution

  • I agree that shadowing of active patterns can be tricky - though it is the same problem that you get with discriminated union cases and record labels in F#. In case of types, you can always include the type name to resolve the ambiguity.

    In case of active patterns, you can put them in modules - for example Pat1 and Pat2:

    module Pat1 =
     let (|A|B|C|) c =
       if (c = 'a') then A
       else if (c = 'b') then B
       else C
    
    module Pat2 =
     let (|A|B|D|) c =
       if (c = '1') then A
       else if (c = '2') then B
       else D
    

    So, in your code, you can then use fully qualified name like Pat1.A or Pat2.A:

    let check myvar =
      match myvar with 
      | Pat1.A -> printf "match A\n"
      | n -> printf "match other %A\n" n