Skip to content

F# Guide — Pattern Matching: Elegant Conditional Logic

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about F# Guide. We cover key concepts, practical examples, and best practices to help you master this topic.

Pattern matching in F# is a powerful feature that checks a value against a series of patterns, decomposing data structures and controlling flow with compile-time verification that all cases are handled.

What You'll Learn

  • Basic match expressions
  • Pattern types: constant, variable, wildcard
  • Decomposing tuples, lists, and records
  • Guard clauses
  • Active patterns for custom matching

Why It Matters

Pattern matching replaces long if-else chains and switch statements with expressive, exhaustive, and safe conditional logic. The compiler warns about unmatched cases. Durga Antivirus Pro uses pattern matching for rule classification.

Real-World Use

Parsing commands, processing ASTs, routing messages, and validating data all benefit from pattern matching's expressiveness.

flowchart LR
    A["Pattern Matching"] --> B["Match Expression"]
    B --> C["Tuple Patterns"]
    C --> D["List Patterns"]
    D --> E["Active Patterns"]
    A:::current --> B
    style A fill:#2563eb,stroke:#2563eb,color:#fff
    style B fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style C fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style D fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style E fill:#f1f5f9,stroke:#94a3b8,color:#64748b

Basic Match

let describeNumber x =
    match x with
    | 0 -> "Zero"
    | 1 -> "One"
    | _ -> "Many"

describeNumber 0  // "Zero"
describeNumber 5  // "Many"

Tuple Patterns

let describePair (a, b) =
    match (a, b) with
    | (0, 0) -> "Origin"
    | (x, 0) -> sprintf "On X axis at %d" x
    | (0, y) -> sprintf "On Y axis at %d" y
    | (x, y) -> sprintf "At (%d, %d)" x y

List Patterns

let describeList lst =
    match lst with
    | [] -> "Empty"
    | [x] -> sprintf "One element: %d" x
    | [x; y] -> sprintf "Two elements: %d and %d" x y
    | x :: _ -> sprintf "Starts with %d" x

describeList [1; 2; 3]  // "Starts with 1"

Record Patterns

type Person = { Name: string; Age: int }

let classify { Name = name; Age = age } =
    match (name, age) with
    | ("Alice", _) -> "Found Alice"
    | (_, age) when age < 18 -> "Minor"
    | (_, age) when age < 65 -> "Adult"
    | _ -> "Senior"

Guard Clauses

let classifyTemperature temp =
    match temp with
    | t when t <= 0 -> "Freezing"
    | t when t <= 20 -> "Cold"
    | t when t <= 30 -> "Warm"
    | t when t <= 40 -> "Hot"
    | _ -> "Extreme!"

Option Types

let describeOptional opt =
    match opt with
    | Some value -> sprintf "Has value: %d" value
    | None -> "No value"

describeOptional (Some 42)  // "Has value: 42"
describeOptional None        // "No value"

Active Patterns

// Define active patterns
let (|Even|Odd|) n =
    if n % 2 = 0 then Even else Odd

// Use them in match
let parity n =
    match n with
    | Even -> "Even"
    | Odd -> "Odd"

// Partial active patterns
let (|Int|_|) (s: string) =
    match System.Int32.TryParse(s) with
    | (true, n) -> Some n
    | _ -> None

Nested Patterns

type Shape =
    | Circle of radius: float
    | Rectangle of width: float * height: float

let area shape =
    match shape with
    | Circle r -> System.Math.PI * r * r
    | Rectangle (w, h) -> w * h

Common Mistakes

1. Incomplete matches

The compiler warns about non-exhaustive matches. Always include a wildcard _ case as fallback.

2. Over-specific ordering

More specific patterns should come before general ones. Patterns are checked top-to-bottom.

3. Confusing = and when

Use = for equality in patterns. Use when for guard clauses with arbitrary conditions.

4. Ignoring the warning for unused patterns

If a pattern can never match (shadowed by earlier pattern), the compiler warns. Reorder or remove dead patterns.

5. Forgetting parentheses in complex patterns

Complex nested patterns may need parentheses for clarity: (x :: rest) vs x :: rest.

Practice Questions

1. What is pattern matching in F#? A feature that checks values against patterns, decomposing data structures and controlling flow with exhaustive checking.

2. What is the wildcard pattern _? It matches any value and is typically used as the default case or when the value is not needed.

3. What are guard clauses? Additional conditions (using when) attached to patterns that must be true for the pattern to match.

Challenge: Write a pattern-matching function that parses a string command ("add 5", "sub 3", etc.) and executes it.

FAQ

{{< faq question="Is pattern matching faster than if-else?" >}} Yes, the F# compiler optimizes pattern matching into efficient IL code, often using jump tables for simple patterns. {{< /faq >}}

{{< faq question="Can I match on type?" >}} Use :? for type tests: match x with | :? string as s -> s | :? int as i -> string i | _ -> "". {{< /faq >}}

{{< faq question="What is active pattern?" >}} Active patterns let you define custom pattern matching classifications, converting between representations during matching. {{< /faq >}}

{{< faq question="Can I use pattern matching in let bindings?" >}} Yes. let (x, y) = (1, 2) destructures a tuple. let [x; y] = [1; 2] destructures a list. {{< /faq >}}

{{< faq question="What happens if no pattern matches?" >> At compile time, the compiler warns if patterns are not exhaustive. At runtime, a MatchFailureException is thrown. {{< /faq >}}

Mini Project

Build a simple calculator using pattern matching:

type Operation = Add | Subtract | Multiply | Divide

let calculate op x y =
    match op with
    | Add -> x + y
    | Subtract -> x - y
    | Multiply -> x * y
    | Divide -> x / y

let result = calculate Add 10 5  // 15

What's Next

Now that you understand pattern matching, explore lists and list operations.

Topic Description Link
F# Lists List operations in F# {{< ref "07-lists" >}}
F# Arrays Array operations {{< ref "08-arrays" >}}

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro