There are few times when I learn a language when I just stop and say “cool”! Active patterns gets this type of response.
Recently I was perusing the F# category on stackoverflow.com when I came across a problem with Discriminated Unions, you can read it here. The problem in a nutshell is two members of the union represent the same type and the individual asking the question wants an easy way to evaluate the types A and B the same, say using distinctBy.
type u = { str : string }
type du =
| A of u
| B of u
Since A and B represent the same data type u it becomes cumbersome to apply the same logic to for A and B in the same match expression. The easiest way is to use A a | B b in a match expression, that is of course without using Active Patterns. To define the Active Pattern used in this exercise I used the following
let (|AorB|) (v:du) =
match v with
| A a -> a
| B b –> b
In essence we define a way to substitute the expression that AorB defines. So for example we want to use a similar expression in a distinctBy you can simply supply the patter (|AorB|) rather than using the verbose match syntax. The following will show both styles of syntax.
Way 1 (More verbose)
[A { str = "A" }; B { str = "B"}; B { str = "A"}]
|> Seq.distinctBy (fun i -> match i with AorB c -> c)
|> Seq.iter (fun i -> match i with AorB c -> printfn "%s" c.str)
Way 2
[A { str = "A" }; B { str = "B"}; B { str = "A"}]
|> Seq.distinctBy (|AorB|)
|> Seq.iter (fun i -> match i with AorB c -> printfn "%s" c.str)
While we only save ourselves the use of a single ‘fun’ expression, the implication is quite staggering should the pattern be more than two types of evaluations. Nice!
For more about Discriminated Unions check here, and more about Active Patterns check here.