F# Guide — Pattern Matching: Elegant Conditional Logic
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