layout: post
title: “Active patterns”
description: “Dynamic patterns for powerful matching”
nav: why-use-fsharp
seriesId: “Why use F#?”
seriesOrder: 17

categories: [Convenience, Patterns]

F# has a special type of pattern matching called “active patterns” where the pattern can be parsed or detected dynamically. As with normal patterns, the matching and output are combined into a single step from the caller’s point of view.

Here is an example of using active patterns to parse a string into an int or bool.

  1. // create an active pattern
  2. let (|Int|_|) str =
  3. match System.Int32.TryParse(str) with
  4. | (true,int) -> Some(int)
  5. | _ -> None
  6. // create an active pattern
  7. let (|Bool|_|) str =
  8. match System.Boolean.TryParse(str) with
  9. | (true,bool) -> Some(bool)
  10. | _ -> None

You don’t need to worry about the complex syntax used to define the active pattern right now ? this is just an example so that you can see how they are used.

Once these patterns have been set up, they can be used as part of a normal “match..with“ expression.

  1. // create a function to call the patterns
  2. let testParse str =
  3. match str with
  4. | Int i -> printfn "The value is an int '%i'" i
  5. | Bool b -> printfn "The value is a bool '%b'" b
  6. | _ -> printfn "The value '%s' is something else" str
  7. // test
  8. testParse "12"
  9. testParse "true"
  10. testParse "abc"

You can see that from the caller’s point of view, the matching with an Int or Bool is transparent, even though there is parsing going on behind the scenes.

A similar example is to use active patterns with regular expressions in order to both match on a regex pattern and return the matched value in a single step.

  1. // create an active pattern
    open System.Text.RegularExpressions
    let (|FirstRegexGroup|_|) pattern input =
    let m = Regex.Match(input,pattern)
    if (m.Success) then Some m.Groups.[1].Value else None

Again, once this pattern has been set up, it can be used transparently as part of a normal match expression.

  1. // create a function to call the pattern
  2. let testRegex str =
  3. match str with
  4. | FirstRegexGroup "http://(.*?)/(.*)" host ->
  5. printfn "The value is a url and the host is %s" host
  6. | FirstRegexGroup ".*?@(.*)" host ->
  7. printfn "The value is an email and the host is %s" host
  8. | _ -> printfn "The value '%s' is something else" str
  9. // test
  10. testRegex "http://google.com/test"
  11. testRegex "alice@hotmail.com"

And for fun, here’s one more: the well-known FizzBuzz challenge written using active patterns.

  1. // setup the active patterns
  2. let (|MultOf3|_|) i = if i % 3 = 0 then Some MultOf3 else None
  3. let (|MultOf5|_|) i = if i % 5 = 0 then Some MultOf5 else None
  4. // the main function
  5. let fizzBuzz i =
  6. match i with
  7. | MultOf3 & MultOf5 -> printf "FizzBuzz, "
  8. | MultOf3 -> printf "Fizz, "
  9. | MultOf5 -> printf "Buzz, "
  10. | _ -> printf "%i, " i
  11. // test
  12. [1..20] |> List.iter fizzBuzz