F# Avoid active pattern overwriting

142 views Asked by At

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?

2

There are 2 answers

0
Tomas Petricek On BEST ANSWER

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
0
Daniel On

I think your concerns apply to shadowing in general and not just active patterns. How often will you define two active patterns whose parameters and return values are the same and have overlapping case names? Generally, types mitigate potential shadowing problems. Along these lines, type annotations are your friend.