(|Node|Leaf|) is defining an active pattern. In more detail, it creates a function and binds it to the identifier "(|Node|Leaf|)". The type of this function is

1
val (|Node|Leaf|) : #XmlNode -> Choice<string * seq<XmlNode>, string>

Where Choice is defined in the F# library and overloaded for multiple type arguments.

1
2
3
type Choice<'a,'b> = Choice2_1 of 'a | Choice2_2 of 'b
type Choice<'a,'b,'c> = Choice3_1 of 'a | Choice3_2 of 'b | Choice3_3 of 'c
...

Choice is like a generic, anonymous union type used to hold the result of an active pattern, so no new types are defined. In the scope of the active pattern definition, the compiler maps the tags of the active pattern (Node, Leaf) to tags of the Choice type (Choice2_1, Choice2_2).

By on 6/5/2008 5:57 PM ()

2.) #System.Xml.XmlNode Why is there a "#" infront of the class name?

Normally, when a function parameter has a type constraint, e.g., "let f (param:MyType) = ...", then that parameter must exactly match the specified type at the call site. This would frequently involve a cast at the call site, e.g. "let g = f (a:>MyType)".

However, when a hash (#) preceeds a parameter type, it indicates that the parameter can accept values of either the specified type, or of types derived from that type, e.g.

1
2
3
let f (param:#MyType) = () // do something
let a = new MySubType()
let g = f a

1.) (|Node|Leaf|) What exactly is happening here? Is this like defining new types?

That construct declares an active pattern. Active patterns can be used to greatly enhance the usefulness of match expressions. This particular pattern declares two pattern matchers: Node and Leaf. These can be used with an XML DOM document to help extract information using a very succinct syntax:

1
2
3
4
5
// Off the top of my head - not verified...
match xml with
| Node "NodeA" _ -> printf "Got <NodeA> tag"
| Node "NodeB" _ -> printf "Got <NodeB> tag"
| Leaf  s -> printf "Inner text: %s" s

See the excellent, but quite technical, Combining Total and Ad Hoc Extensible Pattern Matching in a Lightweight Language Extension, by Don Syme, James Margetson, and Gregory Neverov. My favorate active pattern from the draft of that paper allows for matching with regular expressions which, frankly, should be a standard F# language feature, since it's so easy to implement as an active pattern:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
open System.Text.RegularExpressions
let (|ParseRegex|_|) re s =
    let m = Regex(re).Match(s) in
    if m.Success
    then Some m
    else None

let file = Seq.of_list [ "a"; "Date=1/1/2008"; "b"; "Date=2/22/2008" ]

// Filter out lines starting with "Date=..."
file
|> Seq.choose (function
   | ParseRegex "^Date=(.*)$" m -> Some( m.Value )
   | _ -> None)
|> Seq.iter (fun date -> printfn "Found date: %s" date)

Also see the following posts:

(sorry if I've missed your post... this list is from the first few pages when googling "F# active pattern")

-- Jay

By on 6/5/2008 5:22 PM ()

Thanks for your response Jay!

I suppose the one thing I'm still having a bit of trouble with is what makes Active Patterns more powerful than just declaring a union type and pattern matching over that union type? Obviously there's something I'm missing. I'll keep reading! :)

Thanks again!

By on 6/5/2008 5:45 PM ()

Active patterns are more powerful than matching a union type because the allow a user-defined function to execute as part of the pattern match and thereby allowing you to match data that is not a union type, match union type data in a different way, and parameterize patterns.

For example, without active patterns you could not match the structure of an XmlNode in any meaningful way. This is because XmlNode is not a union type and never will be because it is defined outside of F#, but by defining an active pattern you can make this non-union datatype look like a union datatype and consume it through pattern matching.

By on 6/5/2008 6:06 PM ()
IntelliFactory Offices Copyright (c) 2011-2012 IntelliFactory. All rights reserved.
Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us | Terms of Use | Privacy Policy | Cookie Policy
Built with WebSharper