Formal Software Verification

6.3. Pattern Matching Expressions🔗

Lecture 3 wrote a function as top-level equations, one per constructor shape. The same power is available as a match expression usable wherever a term is expected, and as if c then … else … for a decidable condition. A match elaborates through the type's recursor, its casesOn of §6.1 in the simplest cases, and if branches on a Decidable instance for its condition. Patterns are tried top to bottom, so an earlier pattern shadows a later one, and the wildcard _ matches anything.

namespace Func def classify (n : ) : String := match n with | 0 => "zero" | 1 => "one" | _ => "many" def pred? : Option | 0 => none | n + 1 => some n end Func

The Option type packages a partial result, some x for a value and none for its absence, so pred? is total although the predecessor of 0 is undefined.

6.3.1. Examples🔗

The examples below use match and if inside a body, on numbers, pairs, lists, and options.

Example 1. A match returning a classification, its wildcard catching every remaining case.

namespace Func example : classify 7 = "many" := rfl end Func

Example 2. if tests a decidable condition, here whether a number is zero.

namespace Func def isZero (n : ) : Bool := if n = 0 then true else false example : isZero 0 = true := rfl end Func

Example 3. A match on a pair inspects both components at once.

namespace Func def bothZero (p : × ) : Bool := match p with | (0, 0) => true | _ => false example : bothZero (0, 3) = false := rfl end Func

Example 4. pred? returns none at zero, so the result is always defined.

namespace Func example : pred? 0 = none := rfl end Func

Example 5. A match on a list returns the head as an option.

namespace Func def firstOpt {α : Type} : List α Option α | [] => none | x :: _ => some x example : firstOpt [3, 1] = some 3 := rfl end Func

Example 6. A match on an option unpacks some and supplies a default for none.

namespace Func def orZero : Option | none => 0 | some n => n example : orZero (some 5) = 5 := rfl end Func

Example 7. Patterns are tried top to bottom, so the specific case precedes the wildcard.

namespace Func def sign (n : ) : String := match n with | 0 => "zero" | _ => "nonzero" example : sign 0 = "zero" := rfl end Func

Example 8. A match may appear inside a larger term, here inside an addition.

namespace Func def bump (o : Option ) : := 1 + (match o with | none => 0 | some n => n) example : bump (some 4) = 5 := rfl end Func

Example 9. if and a two-branch match decide the same condition.

namespace Func def isZeroMatch (n : ) : Bool := match n with | 0 => true | _ => false example : isZeroMatch 0 = isZero 0 := rfl end Func

Example 10. A safe division returning none when the divisor is zero.

namespace Func def safeDiv (m n : ) : Option := if n = 0 then none else some (m / n) example : safeDiv 6 0 = none := rfl end Func