Practical Guide to F# Active Patterns: Simplify Pattern Matching in Your Code
Introduction
The first time I saw an active pattern inside an F# project, I was scratching my head (I can't figure it out). However, the longer I stared at it, I realized it was a function, but a special kind of function.
Let's try to discuss it here.
It is also recommended to know what pattern matching is in F#. I have a previous article about pattern matching; you can click here.
Okay, let's get started.
What is an Active Pattern?
In F#, an active pattern is a language feature that allows developers to define custom pattern-matching operations.
Active patterns enable developers to extract data from complex or custom types, making our code expressive and maintainable.
Syntax of Active Pattern
let (|PatternName|) (param: paramType) =
// Pattern matching logic to extract data from paramType
// Optionally, return Some result or None
Let's try to see the breakdown of the syntax.
- The
|PatternName|
is the name of the active pattern you have defined. As you notice, it should start and end with a vertical bar|
. - The
param
is the type used for pattern matching. - The
paramType
is the type that the active pattern takes as input.
Active Pattern Example
Let's try to see a simple example.
let (|Morning|Afternoon|Evening|Night|) (hour:int) =
match hour with
| h when h >= 5 && h < 12 -> Morning
| h when h >= 12 && h < 17 -> Afternoon
| h when h >= 17 && h < 21 -> Evening
| h -> Night
The example code above shows an active pattern that checks whether the hour of the day is morning, afternoon, evening, or night.
Let's create another function that will use the |Morning|Afternoon|Evening|Night|
function.
let showTimeCategory (hour: int) : string =
match hour with
| Morning -> sprintf "%d:00 is in the morning." hour
| Afternoon -> sprintf "%d:00 is in the afternoon." hour
| Evening -> sprintf "%d:00 is in the evening." hour
| Night -> sprintf "%d:00 is at night." hour
Again, the code example above shows that it will return a string indicating the day's hour and shows if it is morning, afternoon, evening, or night.
Let's see it in full action with the unit test.
namespace FsharpActivePatternTest
open System
open Xunit
module SampleActivePattern =
let (|Morning|Afternoon|Evening|Night|) (hour:int) =
match hour with
| h when h >= 5 && h < 12 -> Morning
| h when h >= 12 && h < 17 -> Afternoon
| h when h >= 17 && h < 21 -> Evening
| h -> Night
let showTimeCategory (hour: int) : string =
match hour with
| Morning -> sprintf "%d:00 is in the morning." hour
| Afternoon -> sprintf "%d:00 is in the afternoon." hour
| Evening -> sprintf "%d:00 is in the evening." hour
| Night -> sprintf "%d:00 is at night." hour
module TestActivePattern =
[<Fact>]
let ``Test showTimeCategory Morning``() =
let time = new TimeOnly(6,0)
let result = SampleActivePattern.showTimeCategory(time.Hour)
Assert.True("morning" |> result.Contains)
[<Fact>]
let ``Test showTimeCategory Afternoon`` () =
let time = new TimeOnly(13,0)
let result = SampleActivePattern.showTimeCategory(time.Hour)
Assert.True("afternoon" |> result.Contains)
[<Fact>]
let ``Test showTimeCategory Evening`` () =
let time = new TimeOnly(18,0)
let result = SampleActivePattern.showTimeCategory(time.Hour)
Assert.True("evening" |> result.Contains)
[<Fact>]
let ``Test showTimeCategory Night`` () =
let time = new TimeOnly(22,0)
let result = SampleActivePattern.showTimeCategory(time.Hour)
Assert.True("night" |> result.Contains)
Types of Active Pattern
There are two types of active patterns: the active pattern and the partial active pattern.
Let's try to discuss them one by one.
Active Pattern
- It is used mainly on direct match expression to deconstruct data and perform pattern matching.
- Complete functions that provide complete pattern matching and use the standard syntax
let (|PatterName|) (param:paramType) = ...
format.
Partial Active Pattern
- As the name suggests, these partial functions provide custom pattern matching for a specific data type.
- It uses a wildcard character (
_
) to define a partial active pattern and use this syntaxlet (|PatternName|_|) (param:paramType) = ...
format.
Partial Active Pattern Example
Let's try to see another example.
let (|FirstRegexMatchGroup|_|) (input:string , pattern:string):Option<string> =
let matched = Regex.Match(input,pattern)
if(matched.Success) then Some matched.Groups[0].Value else None
The code sample above will let you set the input string and the pattern for your regular expression. Once there's a match, it will get the first group of that regular expression.
Now, let's create another function to check if the input is a valid email.
let IsEmailValid (str:string) =
match str , @"^[\w\-\.]+@([\w-]+\.)+[\w-]{2,}$" with
| FirstRegexMatchGroup host ->
sprintf "Your is email is valid. You may now proceed using this email: %s" host
| _ -> sprintf "Not a valid email"
Easy to understand, right?
Let's create a unit test for this function, see the whole code, and see how it works under the hood.
module SamplePartialActivePattern =
let (|FirstRegexMatchGroup|_|) (input:string , pattern:string):Option<string> =
let matched = Regex.Match(input,pattern)
if(matched.Success) then Some matched.Groups[0].Value else None
let IsEmailValid (str:string) =
match str , @"^[\w\-\.]+@([\w-]+\.)+[\w-]{2,}$" with
| FirstRegexMatchGroup host ->
sprintf "Your is email is valid. You may now proceed using this email: %s" host
| _ -> sprintf "Not a valid email"
module TestPartialActivePattern =
[<Fact>]
let ``Test Input Valid Email`` () =
let url = "jin.necesario@uap.asia"
let result = url |> SamplePartialActivePattern.IsEmailValid
Assert.True("Your is email is valid" |> result.Contains)
[<Fact>]
let ``Test Input InValid Email`` () =
let url = "hello world"
let result = url |> SamplePartialActivePattern.IsEmailValid
Assert.True("Not a valid email" |> result.Contains)
How's that? Simple right?
Hopefully, you have enjoyed the examples.
Summary
In this post, we have started discussing F#'s active pattern and show a simple example of how to use it for your projects.
Moreover, we have shown another type of active pattern, the partial active pattern, and of course, we have shown examples. Lastly, we have also seen their differences in discussing the types of active patterns.
Stay tuned for more. Until next time, happy programming and happy cloud computing!
Please don't forget to subscribe, bookmark, like, and comment. Cheers! and Thank you!