F# Training
F# Training
F# Training
  • Presentation
  • Fundamentals
    • Introduction
    • Syntax
      • Bases
      • Functions
      • Rules
      • Exceptions
    • First concepts
    • šŸ”Quiz
  • Functions
    • Signature
    • Concepts
    • Syntax
    • Standard functions
    • Operators
    • Addendum
    • šŸ”Quiz
    • šŸ“œSummary
  • Types
    • Overview
    • Tuples
    • Records
    • Unions
    • Enums
    • Anonymous records
    • Value types
    • šŸ“œRecap
    • Addendum
  • Monadic types
    • Intro
    • Option type
    • Result type
    • Smart constructor
    • šŸš€Computation expression (CE)
    • šŸš€CE theoretical basements
    • šŸ“œRecap
  • Pattern matching
    • Patterns
    • Match expression
    • Active patterns
    • šŸš€Fold function
    • šŸ“œRecap
    • šŸ•¹ļøExercises
  • Collections
    • Overview
    • Types
    • Common functions
    • Dedicated functions
    • šŸ”Quiz
    • šŸ“œRecap
  • Asynchronous programming
    • Asynchronous workflow
    • Interop with .NET TPL
    • šŸ“œRecap
  • Module and Namespace
    • Overview
    • Namespace
    • Module
    • šŸ”Quiz
    • šŸ“œRecap
  • Object-oriented
    • Introduction
    • Members
    • Type extensions
    • Class, Struct
    • Interface
    • Object expression
    • Recommendations
Powered by GitBook
On this page
  • Exhaustivity
  • Guard
  • Guard vs OR Pattern
  • Pattern composition
  • Match function
  • Exhaustivity in OOP

Was this helpful?

Edit on GitHub
  1. Pattern matching

Match expression

Similar to a switch expression in C♯ 8.0 but more powerful thanks to patterns

Syntax:

match test-expression with
| pattern1 [ when condition ] -> result-expression1
| pattern2 [ when condition ] -> result-expression2
| ...

Returns the result of the 1st branch whose pattern "matches" test-expression

ā˜ Note: all branches must return the same type!

Exhaustivity

A C# switch must always define a default case. Otherwise: compile warning, šŸ’„ MatchFailureException at runtime

Not necessary in a F# match expression if branches cover all cases because the compiler checks for completeness and "dead" branches

let fn x =
    match x with
    | Some true  -> "ok"
    | Some false -> "ko"
    | None       -> ""
    | _          -> "?"
//    ~  āš ļø Warning FS0026: his rule will never be matched

ā˜ Tip: the more branches are exhaustive, the more code is explicit and safe

Example: checking all the cases of a union type allows you to manage the addition of a case by a warning at compile time: Warning FS0025: Special criteria incomplete in this expression

  • Detection of accidental addition

  • Identification of the code to change to handle the new case

Guard

Syntax: pattern1 when condition Usage: to refine a pattern, using constraints on variables

let classifyBetween low top value =
    match value with
    | x when x < low -> "Inf"
    | x when x = low -> "Low"
    | x when x = top -> "Top"
    | x when x > top -> "Sup"
    | _ -> "Between"

let test1 = 1 |> classifyBetween 1 5  // "Low"
let test2 = 6 |> classifyBetween 1 5  // "Sup"

šŸ’” The guard is only evaluated if the pattern is satisfied.

Guard vs OR Pattern

The OR pattern has a higher precedence/priority than the Guard :

type Parity = Even of int | Odd of int

let parityOf value =
    if value % 2 = 0 then Even value else Odd value

let hasSquare square value =
    match parityOf square, parityOf value with
    | Even x2, Even x
    | Odd  x2, Odd  x
        when x2 = x*x -> true  // šŸ‘ˆ The guard is covering the 2 previous patterns
    | _ -> false

let test1 = 2 |> hasSquare 4  // true
let test2 = 3 |> hasSquare 9  // true

Pattern composition

Pattern matching is powerful not only due to its exhaustivity but also because of the pattern composition.

→ Example:

match feature with
| { Value = FeatureValue.Boolean(Some true) } -> "Enabled"
| _ -> "Disabled"

Notes

This example demonstrates how we match the data on 4 levels:

  1. feature is a record with the Value field (possibly amongst other fields)

  2. Value has the case Boolean of the union type FeatureValue.

  3. This union case contains an optional boolean (bool option type).

  4. We detect when the provided value is true.

Match function

Syntax:

function
| pattern1 [ when condition ] -> result-expression1
| pattern2 [ when condition ] -> result-expression2
| ...

Equivalent to a lambda taking an implicit parameter which is "matched":

fun value ->
    match value with
    | pattern1 [ when condition ] -> result-expression1
    | pattern2 [ when condition ] -> result-expression2
    | ...

Benefits:

  1. In a pipeline

value
|> is123
|> function
    | true  -> "ok"
    | false -> "ko"
  1. Terser function

let is123 = function
    | 1 | 2 | 3 -> true
    | _ -> false

Limitations

Example: a function with both explicit parameters and the function keyword → The number of parameters and their order can be wrong:

let classifyBetween low high = function  // šŸ‘ˆ 3 parameters : `low`, `high`, and another one implicit
    | x when x < low  -> "Inf"
    | x when x = low  -> "Low"
    | x when x = high -> "High"
    | x when x > high -> "Sup"
    | _ -> "Between"

let test1 = 1 |> classifyBetween 1 5  // "Low"
let test2 = 6 |> classifyBetween 1 5  // "Sup"

Exhaustivity in OOP

Visitor is a behavioral design pattern that lets you separate algorithms from the objects on which they operate.

→ It's FP in OOP, much very convoluted.

PreviousPatternsNextActive patterns

Last updated 18 days ago

Was this helpful?

As the parameter is implicit, it can make the code more difficult to understand! → See

The equivalent of the pattern matching exhaustivity in FP is ... the in OOP

Visitor design pattern
Point-free style