Pattern Matching
All about pattern matching in Ocaml.
Powerful features in OCaml, allowing to destructure values by matching them against patterns.
This feature is particularly useful when working with complex data types such as tuples, lists, and variants.
Syntax
In OCaml, the match
expression is used to perform pattern matching. The syntax is as follows:
match expr with
| pattern1 -> result1
| pattern2 -> result2
| _ -> default_result
- The
expr
is evaluated, and its value is compared to each pattern. - If a pattern matches the value, the corresponding result is returned.
- The
_
pattern acts as a "catch-all" for any unmatched cases.
Example
let classify_number n =
match n with
| 0 -> "Zero"
| 1 -> "One"
| _ -> "Other"
This function matches the value of n
and returns the appropriate string.
Patterns
1. Constant Patterns
You can match specific constants such as integers, booleans, and characters.
match true with
| true -> "Yes"
| false -> "No"
2. Variable Patterns
You can bind a value to a variable in a pattern. This is useful when you don't need to match specific values but want to extract them.
let describe_number n =
match n with
| 0 -> "Zero"
| n -> "The number is " ^ string_of_int n
In the second case, the pattern n
matches any number and binds it to the variable n
.
3. Tuple Patterns
You can match on tuples by breaking them down into their components.
let add_tuple t =
match t with
| (a, b) -> a + b
let result = add_tuple (3, 4) (* Returns 7 *)
4. List Patterns
Lists can be matched using []
for the empty list or the ::
(cons) operator to split the head and tail of a list.
let rec sum_list lst =
match lst with
| [] -> 0
| head :: tail -> head + sum_list tail
let result = sum_list [1; 2; 3; 4] (* Returns 10 *)
Here, head :: tail
matches a non-empty list, where head
is the first element and tail
is the rest of the list.
5. Record Patterns
Record fields can be matched directly by specifying the field names. You can match all fields or only a subset.
type person = { name : string; age : int }
let describe_person p =
match p with
| { name = "Alice"; age = _ } -> "This is Alice"
| { name; age } -> name ^ " is " ^ string_of_int age ^ " years old"
6. Variant Patterns
Pattern matching is particularly useful for working with variant types, also known as algebraic data types.
type shape =
| Circle of float
| Rectangle of float * float
let area s =
match s with
| Circle radius -> 3.1415 *. radius *. radius
| Rectangle (width, height) -> width *. height
7. As Patterns
You can use as
patterns to give a name to the entire value being matched, while also breaking it down into its components.
let rec print_list lst =
match lst with
| [] -> ()
| (head :: tail) as full_list ->
print_endline ("Full list: " ^ String.concat ", " (List.map string_of_int full_list));
print_list tail
8. Or Patterns
You can use |
to match multiple patterns at once.
let is_weekend day =
match day with
| "Saturday" | "Sunday" -> true
| _ -> false
9. Wildcard Pattern (_
)
The underscore (_
) is a wildcard pattern that matches any value. It is often used as a catch-all case in pattern matching.
match n with
| 0 -> "Zero"
| 1 -> "One"
| _ -> "Other"
Pattern Matching with Guards
You can add extra conditions to a pattern match using guards (when
clauses). This allows you to further refine the match based on the value.
let classify_number n =
match n with
| n when n > 0 -> "Positive"
| n when n < 0 -> "Negative"
| _ -> "Zero"
Here, when
is used to check additional conditions beyond just matching a pattern.
Exhaustiveness and Warnings
OCaml will warn you if your pattern matching is not exhaustive, meaning that there are possible inputs for which the match doesn't handle. It is good practice to always include a _
case or ensure you cover all possible values.
let handle_option o =
match o with
| Some value -> value
| None -> 0 (* Handles all cases of the 'option' type *)
Nested Pattern Matching
You can nest patterns to match deeply structured data.
type nested_list =
| Element of int
| List of nested_list list
let rec sum_nested lst =
match lst with
| Element x -> x
| List xs -> List.fold_left (fun acc x -> acc + sum_nested x) 0 xs
let result = sum_nested (List [Element 1; List [Element 2; Element 3]; Element 4]) (* Returns 10 *)
Pattern Matching with Exceptions
Pattern matching can also be used to handle exceptions.
let safe_divide x y =
try x / y
with Division_by_zero -> 0
In this example, the try ... with
construct catches the Division_by_zero
exception and returns a default value.
Pattern Matching on Options
The option
type in OCaml is commonly used to represent values that may or may not exist, and pattern matching is a convenient way to work with it.
let get_default opt default_value =
match opt with
| Some value -> value
| None -> default_value
This function returns the value inside an option
, or a default value if it is None
.