F Sharp Programming/Pattern Matching Basics

Pattern matching is used for control flow; it allows programmers to look at a value, test it against a series of conditions, and perform certain computations depending on whether that condition is met. While pattern matching is conceptually similar to a series of  statements in other languages, F#'s pattern matching is much more flexible and powerful.

Pattern Matching Syntax
In high level terms, pattern matching resembles this:

Each  defines a condition, the   means "if the condition is true, return this value...". The  is the default pattern, meaning that it matches anything, sort of like a wildcard.

Using a real example, it's easy to calculate the nth Fibonacci number using pattern matching syntax:

We can experiment with this function in fsi:

It's possible to chain together multiple conditions which return the same value. For example:

Alternative Pattern Matching Syntax
Pattern matching is such a fundamental feature that F# has a shorthand syntax for writing pattern matching functions using the  keyword: It may not be obvious, but a function defined in this way actually takes a single input. Here's a trivial example of the alternative syntax:

Although it doesn't appear as if the function takes any parameters, it actually has the type, so it takes a single   parameter and returns a. You call this function in exactly the same way that you'd call any other function:

You can add additional parameters to the definition:

Calling this gives:

Note that the added parameters in the call come before the implicit parameter against which the match is made.

Comparison to Other Languages
F#'s pattern matching syntax is subtly different from "switch statement" structures in imperative languages, because each case in a pattern has a return value. For example, the  function is equivalent to the following C#:

Like all functions, pattern matches can only have one return type.

Binding Variables with Pattern Matching
Pattern matching is not a fancy syntax for a  structure found in other languages, because it does not necessarily match against values, it matches against the shape of data.

F# can automatically bind values to identifiers if they match certain patterns. This can be especially useful when using the alternative pattern matching syntax, for example:

The variable  is a pattern. If the  function is called with a , the   and   patterns will fail, but the last pattern will match and bind the value to the identifier.


 * Note to beginners: variable binding in pattern matching often looks strange to beginners, however it is probably the most powerful and useful feature of F#. Variable binding is used to decompose data structures into component parts and allow programmers to examine each part; however, data structure decomposition is too advanced for most F# beginners, and the concept is difficult to express using simple types like s and  s. This book will discuss how to decompose data structures using pattern matching in later chapters.

Using Guards within Patterns
Occasionally, it's not enough to match an input against a particular value; we can add filters, or guards, to patterns using the  keyword:

The function above returns the sign of a number: -1 for negative numbers, +1 for positive numbers, and '0' for 0:

Variable binding is useful because it's often required to implement guards.

Pay Attention to F# Warnings
Note that F#'s pattern matching works from top to bottom: it tests a value against each pattern, and returns the value of the first pattern which matches. It is possible for programmers to make mistakes, such as placing a general case above a specific (which would prevent the specific case from ever being matched), or writing a pattern which doesn't match all possible inputs. F# is smart enough to notify the programmer of these types of errors.

Example With Incomplete Pattern Matches

While this code is valid, F# informs the programmer of the possible error. F# warns us for a reason: F# will throw an exception if a pattern isn't matched. The obvious solution to this problem is to write patterns which are complete.

On occasions when a function genuinely has a limited range of inputs, its best to adopt this style: This function now matches any possible input, and will fail with an explanatory informative error message on invalid inputs (this makes sense, because who would rent a negative 42 bedroom apartment?).

Example With Unmatched Patterns

Since the pattern  matches anything, and since F# evaluates patterns from top to bottom, its not possible for the code to ever reach the pattern.

Here is a demonstration of this code in fsi: The first two lines return the correct output, because we've defined a pattern for  and nothing for.

However, the third line is wrong. We have an entry for, but F# never reaches it. The best solution to this problem is to deliberately arrange the order of conditions from most specific to most general.


 * Note to beginners: The code above contains an error, but it will not throw an exception. These are the worst kinds of errors to have, much worse than an error which throws an exception and crashes an app, because this error puts our program in an invalid state and silently continues on its way. An error like this might occur early in a program's life cycle, but may not show its effects for a long time (it could take minutes, days, or weeks before someone notices the buggy behavior). Ideally, we want buggy behavior to be as "close" to its source as possible, so if a program enters an invalid state, it should throw an exception immediately. To prevent this sort of problem it is usually a good idea to set the compiler flag that treats all warnings as errors; then the code will not compile thus preventing the problem right at the beginning.