F# tips weekly #2: Single case pattern match

Every F# programmer is likely familiar with pattern matching using the match keyword, as illustrated below:

match x with
| Some 0 -> "Zero"
| Some n when n > 0 -> "Positive"
| Some n when n < 0 -> "Negative"
| None -> "Unknown"

However, a less well-known fact is that pattern matching can be employed in various other contexts. It can be utilized in:

Full pattern matches with multiple cases

  • Using the match keyword

  • Using the function keyword

Single-case patterns

  • As parameters in function definitions

  • In lambda function definitions, where parameters come after the fun keyword

  • On the left side of let bindings

Even seemingly simple constructs, such as:

let a, b = pair

or

let foo (a, b) = ...

utilize single-case pattern matching to destructure tuples.

As demonstrated in Tip #1, this technique can also be applied to single-case Discriminated Unions (DUs). However, attempting to use an incomplete pattern match results in a warning. For instance:

let [ x; y ] = someList

produces a warning stating:

Incomplete pattern matches on this expression. For example, the value '[;;_]' may indicate a case not covered by the pattern(s).

Converting this into a complete pattern match is possible only by rewriting it using match or function. Nevertheless, in quick scripts where warnings and the possibility of exceptions are acceptable, this method provides a convenient way to destructure lists or arrays.

A powerful combination involves using single-case patterns with single-case active patterns to achieve elegant conversions without the need for intermediate bindings. In a C# interop scenario, for instance, an active pattern can be defined for Option.ofObj to safely handle values that could be null:

let (|FromNull|) = Option.ofObj

let workWithNull (FromNull maybeValue) = ...

In this example, maybeValue is of type option, and the auto-documented parameter indicates that we expect a value that could be null. This approach can be extended to various scenarios such as handling Nullable types, auto-parsing of strings, and validation.

📆
We will delve into active patterns in more detail in future posts.
4
Subscribe to my newsletter

Read articles from Jindřich Ivánek directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Jindřich Ivánek
Jindřich Ivánek

Software developer focused mainly on F#, with a passion for functional programming, problem solving, efficient algorithms and programming languages. I have a strong computer science background, focused mainly in graph theory and graph algorithms; also was intrigued by data structures, complexity, and computability theory. Experienced with backend F# programming. I worked with mathematical optimization (mixed integer programming) and use of statistical methods as well. I contributed to several open source F# projects, mostly around developer tooling.