Thursday, January 5, 2012

F# ≥ C# (Active Pattern & Enum)

F# provides an interesting feature called "active pattern". It converts the input data to something different.  One of the interesting usages is to replace the enum. When I program the enum, I always find frustrated to link enum item to its definition. For example, the following enum define a number enum,
enum Numbers
{
     Odd,
     Even,
}
but it does not specify what is Odd or Even. I tried to use attribute or simple comments, but what I really want to get is to get the definition in F# when see the enum item. When I see the active pattern, my eyes lit up. It is true you can use a non-partial pattern to solve this problem, but you can't put more than 8 items, so I choose to use partial pattern so my system can be expanded easily in the future.

let ( | Even | _ | ) x = if x % 2 = 0 then Some() else None
let ( | Odd  | _ | ) x = if x % 2 <> 0 then Some() else None
let f x =
    match x with
    | Even -> "even"
    | Odd -> "odd"
let r = f 2  //r = "even"
the above sample only return Some() or None. If want to return something more interesting,



let f0 x = x % 2 = 0
let f1 x = x % 2 <> 0
let ( | Even | _ | ) (x:int) = if f0(x) then Some(sign x) else None
let ( | Odd  | _ | ) (x:int) = if f1(x) then Some(sign x) else None
let f (x:int) =
    match x with
    | Even sign -> sprintf "even sign=%d" sign
    | Odd sign -> sprintf "odd sign=%d" sign
let r = f 2

noticed the highlighted "sign" is to hold the return result Math.Sign(x).

One last thing I want to try is to pass the function with Even (or Odd) pattern. The new code is:


let f0 x = x % 2 = 0
let f1 x = x % 2 <> 0
let ( | Even | _ | ) f (x:int) = if f(x) then Some(sign x) else None
let ( | Odd  | _ | ) f (x:int) = if f(x) then Some(sign x) else None
let f (x:int) =
    match x with
    | Even f0 (* you can think x passed in here *) sign -> sprintf "even sign=%d" sign
    | Odd f1 (* you can think x passed in here *) sign -> sprintf "odd sign=%d" sign
let r = f 2

Please note if you define the pattern Even like the following which take the function f as the second parameter.
let ( | Even | _ | ) (x:int) f = if f(x) then Some(Math.Sign(x)) else None
There will be an error. So the parameter order really matters when you defined the pattern.






No comments: