Pattern Matching in F#


Introduction

Pattern matching is idiomatic in functional programming. It's an important tool in F# programming, general constructs that combines decompositions and control. Pattern matching provides a concise but clear and highly expressive way of writing conditional logic. Pattern matching allows you to look at the values of an identifier and then make different computations depending on its value. Pattern matching is like a switch statement in C and C# but its more powerful and flexible than a switch statement.

Pattern matching is used for control flow, it allows a programmer 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 the same as a series of If-then statements in other languages, pattern matching is used to test whether the piece under test has a desired state, find relevant information or substitute parts with other parts. Programs that are written in the functional style tend to be written as a series of transformations applied to the input data.

Pattern matching allows you to analyze the input data and decide which transformation is to be applied to it. Essentially pattern matching takes an input and a set of rules. Each rules tests the input against a pattern and returns a result if they match. If you want to use pattern matching with explicit values, you can use the match and with keywords. This allows you to match with a particular parameter with some conditions. So for just starting out in F# it's important to know your way around patterns and pattern matching.

Syntax for Pattern Matching

//Syntax for Pattern Matching
match expr with
| pat1 -> result1
| pat2 -> result2
| pat3 when expr2 -> result3
| _ -> defaultResult

In the above syntax every (|) defines a condition and the symbol (->) means "if the given condition is true, return the value" and the symbol (_) means it matches with everything as it's the default pattern.

Match Expression

The match expression is introduced by the keyword match, followed by the expression to be matched, followed by the with keyword. Every condition is then written on a separate line, beginning with (|) and indented until the match keyword. Match is an expression not a statement like switch. In F# match conditions are not values, they are patterns. Constant values like 1, 2 and 3 are simple patterns. It also contains some other patterns like a Wildcard Pattern represented by an Underscore. When a match expression is evaluated it runs through the list of conditions checking the arguments matches with a given pattern.

Pattern Matching Example

PatternMatching Example

// Example of LucasTheorem for Pattern Matching
let rec lucasTheorem a =
        match a with
| a when a <= 0 -> failwith "The value should be greater than 0"
| 1 -> 1
| 2 -> 3
| x -> lucasTheorem (a - 1) + lucasTheorem (a - 2)
// call the function and print the results
printfn "(lucasTheorem 2) = %i" (lucasTheorem 2)
printfn "(lucasTheorem 6) = %i" (lucasTheorem 6)
printfn "(lucasTheorem 11) = %i" (lucasTheorem 11)
printfn "(lucasTheorem 12) = %i" (lucasTheorem 12)

Output-

PatternMatching output

In the above example the second two cases are literals; the values 1 and 2 and these will be replaced with the values 1 and 3 respectively. The fourth case will match any value of x greater than 2 and this will call two further calls to the lucasAlgorithm function.

The rules are matched in the order they are defined, and the compiler will issue an error if pattern matching is incomplete, that is if any possible value that will not match with any value.

Different Ways to form Pattern

General Form Type Example
[| pat; ...;pat |] Array Pattern [|"cmd"; arg1; arg2 |]
pat & pat "And" pattern [p]  [point(a,b)]
{id=pat; ...; id=pat} Record pattern {a=1; b=2}
[pat; ...;pat] List pattern [a;b;c]
pat | pat "Or" pattern [a] | ["A";a]
(pat, ... ,pat) Tuple pattern (1,2("3",a))
Null Null test Pattern Null
id Variable Pattern A
Any literal Constant Pattern 30, "30", 20L, Sysem.DayOfWeek.Monday
pat as id Named Pattern [a] as inp
Tag(pat, ...,pat) Tagged union or active Pattern point(a,b)
_ Wildcard Pattern _
:? Type Type test Pattern :? string

Use of When in Pattern Matching

You can add a when guard to give precise control when a rule fires. A when guard is made with the keyword When followed by a Boolean expression. Once a rule is matched the when clause is evaluated and the rule will fire only when the expression evaluates to true. The first part of the rule is an identifier that will match any integer but the when guard means the rule will match only those integers that are less than or equal to 0.

This is beneficial when the pattern matching is small and you want to fit it on one line.

Example-

let booleanToString x =
match x with false -> "False" | _ -> "True"

Example for Pattern Matching over Tuples, with Boolean "or" and "and" function pattern matching.

Tuple PatternMatching
//example for Tuple pattern matching with "and" and "or"
let oneOr a1 a2 =
    match a1, a2 with
| true, _ -> true
| _, true -> true
| _ ->
false
let oneAnd s =
    match s with
| true, true -> true
| _ -> false
printfn "(oneOr true false) = %b" (oneOr true false)
printfn "(oneOr false false) = %b" (oneOr false false)
printfn "(oneAnd (true, false)) = %b" (oneAnd (true, false))
printfn "(oneAnd (true, true)) = %b" (oneAnd (true, true))

Output-

Tuple PatternMatching Output

Summary

In this article I have covered Pattern Matching and different forms of patterns.


Similar Articles