Pattern Matching
Pattern matching is the primary way to deconstruct data in IML. It is exhaustive — the compiler ensures you handle every possible case.
Basic matching
Use match ... with to match on values:
let describe_color = function
| Red -> "red"
| Green -> "green"
| Blue -> "blue"
let area = function
| Circle r -> Real.(3.14159 * r * r)
| Rectangle (w, h) -> Real.(w * h)
| Triangle (a, b, c) ->
let s = Real.((a + b + c) / 2.0) in
Real.sqrt Real.(s * (s - a) * (s - b) * (s - c))The function keyword is shorthand for fun x -> match x with.
Matching on lists
Pattern matching on lists is especially common:
let rec sum = function
| [] -> 0
| x :: xs -> x + sum xs
let head_or_default default = function
| [] -> default
| x :: _ -> x
let rec zip xs ys =
match xs, ys with
| [], _ | _, [] -> []
| x :: xs', y :: ys' -> (x, y) :: zip xs' ys'Nested patterns
Patterns can be nested to match deeper structure:
let rec is_sorted = function
| [] -> true
| [_] -> true
| x :: x' :: xs -> x <= x' && is_sorted (x' :: xs)
let rec flatten = function
| Leaf a -> [a]
| Node (Leaf a, Leaf b) -> [a; b] (* Special case *)
| Node (l, r) -> flatten l @ flatten rGuards with when
Add conditions to patterns using when:
let rec num_occurs x = function
| [] -> 0
| hd :: tl when hd = x -> 1 + num_occurs x tl
| _ :: tl -> num_occurs x tl
let classify_temp t =
match t with
| t when t < 0 -> "freezing"
| t when t < 20 -> "cold"
| t when t < 30 -> "comfortable"
| _ -> "hot"Wildcard and variable patterns
_matches anything and discards the value- A variable name matches anything and binds the value
(* Ignore second element *)
let first (a, _, _) = a
(* Match any constructor *)
let is_leaf = function
| Leaf _ -> true
| _ -> falseOr-patterns
Match multiple patterns with the same body:
let is_weekend = function
| "Saturday" | "Sunday" -> true
| _ -> false
let is_binary = function
| 0 | 1 -> true
| _ -> falseExhaustiveness
IML requires pattern matching to be exhaustive: every possible value must be handled. The compiler will warn (or error) if you miss a case:
(* This is complete — all constructors handled *)
let show = function
| Red -> "red"
| Green -> "green"
| Blue -> "blue"
(* Missing Blue would be an error *)Exhaustiveness is essential for formal reasoning — it guarantees your functions are total (defined on all inputs).
Matching on records
Destructure records in patterns:
let distance { x; y } =
Real.sqrt Real.(x * x + y * y)
let is_origin { x; y } =
Real.(x = 0.0 && y = 0.0)Matching on tuples
let max_pair = function
| (a, b) when a >= b -> a
| (_, b) -> b
let both_positive (x, y) = x > 0 && y > 0