oCamlCase

The match expression

A match expression compares a value against a sequence of patterns and evaluates the branch of the first one that fits. Patterns can match specific values, bind names, or use a wildcard to catch anything that did not match earlier cases.

Every branch must return the same type. If you forget a case, the compiler warns you. If you add an impossible case that can never be reached, the compiler tells you that too.

The underscore _ is the wildcard pattern. It matches any value and discards it. A branch using _ is typically placed last to catch everything not covered by specific patterns above it.

Key point

OCaml checks that your patterns are exhaustive, meaning all possible values are covered. A non-exhaustive match is a compile-time warning, not a silent runtime failure.

match.ml
let describe n =
  match n with
  | 0 -> "zero"
  | 1 -> "one"
  | _ -> "many"

(* Bind the matched value to a name *)
let double_or_zero n =
  match n with
  | 0 -> 0
  | x -> x * 2

(* Match on lists *)
let rec sum = function
  | []      -> 0
  | x :: xs -> x + sum xs

The function keyword

Writing match requires naming the value you want to inspect. When a function's whole purpose is to immediately match its last argument, you can use the function keyword as shorthand. It combines fun x -> match x with into a single form.

The two definitions below are identical in behavior. The function form is more concise when the matching is the main logic, and it is idiomatic OCaml for recursive functions over lists.

function_kw.ml
(* Long form *)
let describe n =
  match n with
  | 0 -> "zero"
  | 1 -> "one"
  | _ -> "many"

(* Shorthand using function keyword, same result *)
let describe = function
  | 0 -> "zero"
  | 1 -> "one"
  | _ -> "many"

Variant types and exhaustiveness

Variant types (also called algebraic data types or sum types) let you define a type as one of several named forms. Pattern matching on a variant is where exhaustiveness really pays off: when you add a new variant, every match expression that covers that type will get a compiler warning until the new case is handled.

This makes refactoring safe. You cannot forget to update a function when the data model changes.

Each variant constructor can carry data. The Circle variant carries a radius, and Rectangle carries width and height. Pattern matching unpacks that data in the same step as the branch selection.

Good practice

Avoid the wildcard _ when matching variants. Matching each constructor explicitly means adding a new variant will produce a compiler warning instead of silently falling through to a default case.

variants.ml
type shape =
  | Circle    of float
  | Rectangle of float * float
  | Triangle  of float * float

let area = function
  | Circle r         -> 3.14 *. r *. r
  | Rectangle (w, h) -> w *. h
  | Triangle  (b, h) -> 0.5 *. b *. h

(* If you add a new variant here, area will
   get a compile-time warning until updated. *)

Guards

A guard is an extra condition attached to a pattern with the when keyword. The branch only fires if both the pattern matches and the guard evaluates to true. Guards let you add arbitrary boolean conditions to a case without nesting an if expression inside the branch.

guards.ml
let classify x =
  match x with
  | 0            -> "zero"
  | n when n < 0 -> "negative"
  | n when n > 100 -> "large"
  | _            -> "positive"

(* Guards work on any pattern, not just literals *)
let safe_head = function
  | [] -> None
  | x :: _ when x > 0 -> Some x
  | _ -> None

Nested patterns

Patterns can be nested to any depth. You can match on the structure of a tuple whose elements are themselves option values, or on a list whose head is a specific variant. The compiler still verifies that all combinations are covered.

nested.ml
(* Match on a pair of option values *)
let both pair =
  match pair with
  | (Some x, Some y) -> Some (x + y)
  | (Some x, None)   -> Some x
  | (None,   Some y) -> Some y
  | (None,   None)   -> None

(* Match on specific list shapes *)
let first_two = function
  | x :: y :: _ -> Some (x, y)
  | _           -> None
Watch out

Patterns are checked top to bottom and the first match wins. An earlier pattern can shadow a later one. If you place _ before specific cases, those cases will never be reached and the compiler will warn you.