Formal Software Verification

3.3. Functions by Pattern Matching and Recursion🔗

A function on an inductive type is defined by pattern matching, one equation per constructor shape. Recursion is structural when each recursive call peels off one constructor, and Lean accepts such definitions, since they terminate. The definitions below work on the core ℕ, whose constructors are Nat.zero and Nat.succ.

def add : | m, Nat.zero => m | m, Nat.succ n => Nat.succ (add m n) def mul : | _, Nat.zero => 0 | m, Nat.succ n => add m (mul m n) def power : | _, Nat.zero => 1 | m, Nat.succ n => mul m (power m n)

Patterns are richer than bare constructors. The Fibonacci function matches zero, one, and every number of the shape n + 2, which abbreviates two applications of succ.

def fib : | 0 => 0 | 1 => 1 | n + 2 => fib (n + 1) + fib n

An argument that no equation inspects can move to the left of the colon, where it becomes a parameter fixed across the recursion.

def powerParam (m : ) : | Nat.zero => 1 | Nat.succ n => mul m (powerParam m n)

3.3.1. Examples🔗

The examples below define functions by pattern matching and structural recursion on ℕ and on Bool.

Example 1. Halving discards one from every pair, matching the shape n + 2.

def half : | 0 => 0 | 1 => 0 | n + 2 => half n + 1

Example 2. A non-recursive definition needs no pattern matching. Squaring reuses mul.

def square (n : ) : := mul n n

Example 3. Testing for zero returns a Bool, and the two equations cover the two constructors.

def isZero : Bool | Nat.zero => true | Nat.succ _ => false

Example 4. The factorial recurses on the shape n + 1, and the parameter form keeps the multiplication explicit.

def factorial : | 0 => 1 | n + 1 => mul (n + 1) (factorial n)

Example 5. Pattern matching on two arguments at once. The smaller of two numbers descends on both.

def smaller : | _, 0 => 0 | 0, _ => 0 | m + 1, n + 1 => smaller m n + 1

Example 6. The Lucas numbers follow the Fibonacci recursion from different initial values.

def lucas : | 0 => 2 | 1 => 1 | n + 2 => lucas (n + 1) + lucas n

Example 7. Conjunction on Bool matches only its first argument.

def conj : Bool Bool Bool | true, b => b | false, _ => false

Example 8. Evenness recurses by two, so the recursive call peels two constructors.

def evenb : Bool | 0 => true | 1 => false | n + 2 => evenb n

Example 9. The sum of the first n numbers recurses on n + 1.

def sumTo : | 0 => 0 | n + 1 => (n + 1) + sumTo n

Example 10. Powers of two as an instance of the recursion of power, with the base fixed.

def twoPow : | 0 => 1 | n + 1 => mul 2 (twoPow n)