Wednesday, May 16, 2007

Pattern matching on .NET objects with F# active patterns

One of the most interesting things about F# active patterns, is that it allow the creation pattern matching expressions on existing .NET objects . In this post I'm going to show a couple of examples of using F# pattern matching with common .NET objects.

The "Combining Total and Ad Hoc Extensible Pattern Matching in a Lightweight Language Extension" paper describes this feature and gives lots of useful and representative examples (A draft of this paper was commented here).

Active patterns are officially available starting with version 1.9.1.8, but version 1.9.1.9 was used for these examples.

One the simplest ways to start defining active patterns on .NET objects is to create one that returns the value of a property. For example, the following active pattern extracts the FullName property from System.IO.FileInfo .


let (|FileInfoFullName|) (f:FileInfo) = f.FullName


Now we can using FileInfoFullName this way:


let f = (new FileInfo("ActivePatternsTests.exe"))
in
match f with
| FileInfoFullName n ->
Console.WriteLine("File name: {0}",n)


Also active patterns can be used in conjunction with built in F# patterns for example. Given the following active pattern:


let (|FileInfoNameSections|) (f:FileInfo) =
(f.Name,f.Extension,f.FullName)


We can write:


let foo (f:FileInfo) =
match f with
| FileInfoNameSections(_,".txt",fn) -> ("Text file: "+fn)
| _ -> "No text file"


Also active patterns can be combined. For example:

Given these active pattern definitions:


let (|FileSecurity|) (f:FileInfo) = f.GetAccessControl()

let (|NtAccountOwner|) (f:FileSecurity) =
let o = f.GetOwner((typeof() :
ReifiedType< NTAccount >).result) :?> NTAccount
in o.Value


We can write:


let goo (f:FileInfo) =
match f with
| FileSecurity(NtAccountOwner("MyMachine\\auser")) -> "Yes"
| _ -> "No"


Only one kind of active patterns was used in this post, the article describe several others with more interesting characteristics.

More interesting experiments can be done with other .NET APIs. One nice example is LINQ expression trees.