Friday, January 13, 2012

Get active pattern names

When I studied active pattern in F#, I wanted to get the active pattern names. For example:

the function let (|A|B|) x = ()

has  |A|B| active pattern, what I want to do is to get "A" or "B". I have been digging into the reflection for days, but could not find anything. Thanks to Don, I can use the meta programming concept to get this job done. This sample is important not just it can get active pattern name, but also teaches me to use meta programming if reflection does not work.

    /// A generic routine to get the active pattern case name for the result of applying
    /// active pattern 'q' to a given input.
    let nameGetter (q: Quotations.Expr<'T -> 'U>) =
        let meth =
            match q with
            | Quotations.Patterns.Lambda (_,Quotations.Patterns.Call (_,m,_)) -> m
            | _ -> failwith "usage: nameGetter <@ (|A|B|) @>"
        let names = meth.Name.Split( [| '|' |], System.StringSplitOptions.RemoveEmptyEntries)
        let tagProperty = typeof<'U>.GetProperty "Tag"
        if tagProperty = null then failwith "usage: nameGetter <@ (|Total|Active|Pattern|) @>"
        fun (x:'T) ->
            let res = meth.Invoke(null,[| box x |])
            let tag = tagProperty.GetValue(res, null) :?> int
            if tag < 0 || tag >= names.Length then failwith "unexpected tag"
    let (|A|B|) x =
        match x with
        | 'a' -> A
        | 'b'-> B

    let getName = nameGetter <@ (|A|B|) @>
    getName 'a' // A
    getName 'b' // B

No comments: