Navigate / search

Active Patterns: Single Total With Params (|A|) x

This entry is part 3 of 8 in the series Active Patterns

We move on to the next in our series on Active Patterns, but this time we’re really just covering a slight modification to the Single Total pattern that we covered in the last post.

All the same rules apply, we’re just adding the ability to add parameters to the Active Pattern.

I say ‘parameters’ but in reality I mean ‘additional parameters’. Every Active Pattern has at least one parameter. The ‘x’ in ‘match x with’ has to go somewhere.

Remember the Active Patterns we’ve seen so far are just functions. If you take away the banana clips they are identical to functions. Additional parameters are supported exactly the same way they are with regular functions.

All you need to remember is that the ‘x’ in ‘match x with’ will be passed to the last parameter of the Active Pattern. The other parameters are set by the individual lines in the Pattern Match expression.

An example might help.

Here’s the Active Pattern we used in the last post to count special characters in a string.

let (|SpecialCharacterCount|) (str: string) =
    let specialCharacters = "!£$%^"
    str.ToCharArray()
    |> Array.filter (fun c -> specialCharacters.Contains(c.ToString()))
    |> Array.length

Note the special characters are hard coded. This being a function it’s simple to turn that into a parameter.

let (|SpecialCharacterCount|) (characters: string) (str: string) =
    str.ToCharArray()
    |> Array.filter (fun c -> characters.Contains(c.ToString()))
    |> Array.length

You can add multiple parameters but it’s important to know how they map to the pattern matching expression that will use the Active Pattern.

Here’s code that would match against the old ‘non-parameterized’ Active Pattern

let UseThePattern (str: string) =
    match str with
    | SpecialCharacterCount c -> sprintf "That was %d special characters" c

By contrast here’s the code that uses the parameterized version

let UseThePattern (str: string) =
    match str with
    | SpecialCharacterCount "!£$%^" c -> sprintf "That was %d special characters" c

The ‘str’ in the match expression still gets passed to the last parameter of the Active Pattern, and the return value of the Active Pattern still gets assigned to ‘c’. In place of c you can still use literals just as in the last post.

The only real difference is that the extra arguments are placed between the name of the Active Pattern and the variable or literal value that we match against.

Here’s another sketch to try and hammer this home.

Apologies for spelling this out in such tedious detail but if you get what’s happening here, everything else about Active Patterns will be very simple.

Oh, and that little assignment trick we saw in the last post still works, just pass the arguments needed by the Active Pattern.

let (SpecialCharacterCount "!£$%^" c) = "ABC!"
val c : int = 1

That’s it for this post and for my nemesis the Single Total Active Pattern. Next up we discover the wonders of Partial Active Patterns. We get to rope in another interesting piece of F#, Option Types.

Series Navigation<< Active Patterns: Single Total (|A|)Active Patterns: Partial Application >>

Comments

Shawn Martin
Reply

I’ve been enjoying this series very much. Thank you! A question I have is what might be the best way(s) to turn SpecialCharacterCount in this post into the hard-coded SpecialCharacterCount from the previous post? In other words, do partial application. I tried let (|SpecialCharacterCountNew2|) = (|SpecialCharacterCountGen|) “!£$%^” and it produced something that worked, but has a different a slightly different signature than what I would have expected. Is there an idiomatic way to do this?

Richard
Reply

Hi Shawn,

That’s a great question, and in fact Anthony Brown (@bruinbrown93) tweeted me with EXACTLY that example to point out that partial application is possible.

The approach you’re taking is correct and should work fine. The signature sf very slightly different from the hard coded pattern in the previous post but it behaves in exactly the same way.

If you’re still a little unsure, keep an eye out for the next post, I’ve decided to cover this topic before moving on to partial patterns because I think it fits nicely here.

Thanks for the question.

Leave a comment

name*

email* (not published)

website