Active patterns
Pattern Matching Limits
Limited number of patterns
Impossibility of factoring the action of patterns with their own guard
Pattern1 when Guard1 | Pattern2 when Guard2 -> doπ₯Pattern1 when Guard1 -> do | Pattern2 when Guard2 -> doπ
Patterns are not first-class citizens Ex: a function can't return a pattern β Just a kind of syntactic sugar
Patterns interact badly with an OOP style
Origin of Active Patterns
π Extensible pattern matching via a lightweight language extension βΉοΈ 2007 publication by Don Syme, Gregory Neverov, James Margetson
Integrated into Fβ― 2.0 (2010)
π‘ Ideas
Enable pattern matching on other data structures
Make these new patterns first-class citizens
Syntax
General syntax : let (|Cases|) [arguments] valueToMatch = expression
Function with a special name defined in "bananas"
(|...|)Set of 1..N cases in which to store the
valueToMatchparameter
π‘ Kind of factory function of an "anonymous" union type, defined inline
Types
There are 4 types of active patterns:
1. Simple Total
1
β Yes
β 0+
2. Multiple Total
2+
β Yes
β 0
3. Partial
1
β No
β 0
4. Parametric
1
β No
β 1+
π‘ Partial and total indicate the feasibility of "placing the input value in the box(es)"
Partial: there is not always a corresponding box
Total: there is always a corresponding box β exhaustive pattern
Simple total active pattern
A.k.a Single-case Total Pattern
Syntax: let (|Case|) [...parameters] value = Case [data]
Usage: on-site value adjustment
Can accept parameters β οΈ Usually more difficult to understand
Another example: extracting the polar form of a complex number
Active pattern total multiple
A.k.a Multiple-case Total Pattern
Syntax: let (|Case1|...|CaseN|) value = CaseI [dataI]
β No parametersβ
Partial active pattern
Syntax: let (|Case|_|) value = Some Case | Some data | None
β Returns the type 'T option if Case includes data, otherwise unit option
β Pattern matching is non-exhaustive β a default case is required
Similar example, where active patterns are written with the Option.ofTuple function:
π‘ To see how much more readable the code is, let's write a more low-level version of tryParseBoolean where we see:
Nesting
matchexpressionsDifficulty reading lines 6 and 7 due to double Booleans (
true..false,true..true)
Parametric partial active pattern
Syntax: let (|Case|_|) ...arguments value = Some Case | Some data | None
Example 1: leap year β Year divisible by 4 but not by 100, except if divisible by 400
Exemple 2 : Regular expression
π‘ Usages seen with the next example...
Exemple : Hexadecimal color
Recap
Understanding an active pattern
Understanding how to use an active pattern... ...can be a real intellectual challenge! π΅
π Explanations using the previous examples...
Understanding a total active pattern
β factory function of an "anonymous" union type
Understanding a partial active pattern
β Distinguish parameters (input) from data (output)
Examine the active pattern signature: [...params ->] value -> 'U option
N-1 parameters: active pattern parameters
Last parameter:
valueto matchReturn type:
'U optionβ data of type'Uwhen
unit optionβ no data
β Examples
let (|Integer|_|) (s: string) : int optionUsage
match s with Integer iβi: intis the output data
let (|DivisibleBy|_|) (factor: int) (x: int) : unit optionUsage
match year with DivisibleBy 400β400is thefactorparameter
let (|Regexp|_|) (pattern: string) (value: string) : string list optionUsage
match s with Regexp "#([0-9...)" [ r; g; b ]"#([0-9...)"is thepatternparameter[ r; g; b ]is the output data β’ It's a nested pattern: a list of 3 strings
Exercice : fizz buzz with active pattern
Rewrite this fizz buzz using an active pattern DivisibleBy.
Solution
π‘ The active pattern DivisibleBy 3 does not return any data. It's just the syntactic sugar equivalent of if y |> isDivisibleBy 3 . In such a case, Fβ― 9 allows the Boolean to be returned directly, rather than having to go through the Option type:
Alternative
The 2 solutions are equal. It's a matter of style / personal taste.
In Fβ― 9, no need to do
|> boolToOption.
Active patterns use cases
Factor a guard (see previous fizz buzz exercise)
Wrapping a BCL method (see
(|Regexp|_|)and below).Improve expressiveness, help to understand logic (see below)
Expressiveness with active patterns
Active pattern: first-class citizen
An active pattern β function with metadata β first-class citizen in Fβ―
Last updated
Was this helpful?