Interesting Active Patterns

I wanted to do a quick post about active patterns in F#, specifically the usefulness of Single Total Active Patterns (STAPs?) for transforming and validating data.

One slightly annoying thing about the .NET BCL is the fact that there is no plain Date type. Oftentimes you only care about dealing with the date portion of a DateTime, and if there happens to be a stray time associated with it, any comparison might not work as expected.

In any case, I often want to work with the date and/or time portions separately, so I find myself doing things like this at the top of a method:

let date = dateTime.Date  
let time = dateTime.TimeOfDay  

Of course, in F# we could condense this down to a single line with tuple destructuring:

let date, time = dateTime.Date, dateTime.TimeOfDay  

Another approach is to define some active patterns to deal specifically with dates and times:

let (|Date|) (dateTime:DateTime) = dateTime.Date  
let (|Time|) (dateTime:DateTime) = dateTime.TimeOfDay  

This allows us to rewrite the first snippet as follows:

let (Date date) = dateTime  
let (Time time) = dateTime  

People coming from other languages might mistakenly think Date and Time are types in these declarations, but of course they are our active pattern names. The types of date and time are inferred. Again, we can condense this by using an AND Pattern (&) to combine the pattern matches.

let Date date & Time time = dateTime  

Active patterns and pattern matching in general becomes more powerful when you realize that they're not just limited to match/try ... with constructs, but every let binding and parameter is also a pattern match. So one interesting thing we can do with active patterns is reshape our data directly in a parameter declaration. For example, suppose we have a function that takes a DateTime parameter:

let f (dateTime:DateTime) = ...  

We could extract the date portion right in the parameter declaration:

let f (Date date) = ...  

Or we could bind both the date and time to separate values:

let f (Date date & Time time) = ...  

Note that from the caller's perspective this is still just one parameter. The only strange thing about this is that since our parameter is anonymous, we won't get intellisense for the name of the parameter, or the compiler will give it an auto-generated name like _arg1 if it is in a separate assembly. I don't know of any fix for this other than to use a signature file.

The usage of active patterns on arguments opens up other possibilities such as validation. For example, suppose we were to define the following patterns:

let (|NonEmptyString|) value =  
    if String.IsNullOrEmpty value
    then failwith "String must be non-empty."
    else value

let (|GreaterThanEqual|) comparand value =  
    if value < comparand
    then failwithf "Value must be greater than or equal to %A." comparand
    else value

Now we can use these to define preconditions on our function parameters:

let sayHello (NonEmptyString name) =  
    printfn "Hello, %s." name

let rec factorial (GreaterThanEqual 0 n) =  
    if n = 0 then 1
    else n * factorial (n - 1)

If we violate a precondition, such as by passing a -1 to our factorial function, then it will throw an exception. The only downside is that it doesn't include the argument name. We could pass the argument name as an additional argument to the active pattern, but then that is getting kind of crufty.

I realize some of these examples are probably somewhat contrived, and I'm not sure I would do validation like this in a real application. Still, I hope it expands your view of the ways in which active patterns might be used to write more concise and expressive code.

Luke Sandell

Read more posts by this author.

comments powered by Disqus