CSCI 2041

ADVANCED PROGRAMMING PRINCIPLES

Reasoning about programs:

Lists and beyond

Code specifications

To avoid doubt about “properties of lists” we can state properties using only code:

let rec append l1 l2 = match l1 with
| [] -> l2
| h::t -> h::(append t l2)

let rec sum = function [] -> 0
| h::t -> h + (sum t)

Claim:ℓ₁ : int list:

ℓ₂ : int list,

sum (append ℓ₁ ℓ₂) ≡ (sum ℓ₁) + (sum ℓ₂)

Base Case: ℓ₁ = [].

Inductive Case: Need to show that:

sum (append ℓ₁ ℓ₂) ≡ (sum ℓ₁) + (sum ℓ₂) ⇒
  sum (append (x::ℓ₁) ℓ₂) ≡ (sum (x::ℓ₁)) + (sum ℓ₂)

Lemma Example

let rev lst =
  let rec tail_rev lst acc = match lst with
  | [] -> acc
  | h::t -> tail_rev t (h::acc)
  in tail_rev lst []

To prove that ∀ ℓ : 'a list, rev ℓ ≡ reverse ℓ, we’ll first prove:

Lemma.

  ∀ℓ₁,ℓ₂ : 'a list, (tail_rev ℓ₁ ℓ₂) ≡ (reverse ℓ₁) @ ℓ₂

Base Case: ℓ₁ = [].

Inductive Case: Need to show

tail_rev ℓ₁ ℓ₂ ≡ (reverse ℓ₁) @ ℓ₂ ⇒
    tail_rev (x::ℓ₁) ℓ₂ ≡ (reverse (x::ℓ₁)) @ ℓ₂

Informally we are proving that P(ℓ) is invariant over recursive calls.

Can’t you prove any (even false) thing(s) this way?

Consider “broken” versions of append, tail_rev:

let rec badpend l1 l2 = match l1 with
| [] -> l2
| h::t -> badpend l1 l2

(IH only tells about (badpend t l2))

let rec fail_rev l1 acc = match l1 with
| [] -> []
| h::t -> fail_rev t (h::acc)

(Base case is wrong…)

Generalized Induction

For any inductive type of the form:

type t = C₀ of b (* b is some other type not referring to t *)
| C₁ of b₁*t

The principle of induction for type t is:
For all x : t, P(x) if:

  • v : b, P(C₀ v), and
  • x : t, v : b₁, P(x) ⇒ P(C₁(v,x))

Examples:

  • nat: ∀ n : nat, P(n) if P(Zero) and ∀ m, P(m) ⇒ P(Succ m)
  • 'a list: ∀ ℓ : 'a list, P(ℓ) if
    P([]) and ∀ x : 'a, ∀ ℓ : 'a list, P(ℓ) ⇒ P(x::ℓ)

More Generalized Induction…

type bitlist = C0 | C1 | L0 of bitlist | L1 of bitlist

Principle of structural induction for bitlist:

ℓ : bitlist, P(ℓ) if:

  • P(C0), and
  • P(C1), and
  • ∀ ℓ, P(ℓ) ⇒ P(L0 ℓ), and
  • ∀ ℓ, P(ℓ) ⇒ P(L1 ℓ)

Notice: Here we have two base cases, and two step cases.

let rec bitlen blst = match blst with
| C0 | C1 -> 1
| L0 b | L1 b -> 1 + (bitlen b)

let rec bitweight blst = match blst with
| C0 -> 0
| C1 -> 1
| L0 b -> bitweight b
| L1 b -> 1 + (bitweight b)

Prove:bl : bitlist, bitweight bl <= bitlen bl

Prove:bl : bitlist, bitweight bl <= bitlen bl

Base Cases:

bitweight C0 <= bitlen C0

bitweight C1 <= bitlen C1

Inductive Cases: Need to show

bitweight ℓ <= bitlen ℓ ⇒
    bitweight (L0 ℓ) <= bitlen (L0 ℓ)

and

bitweight ℓ <= bitlen ℓ ⇒
    bitweight (L1 ℓ) <= bitlen (L1 ℓ)

cs2041.org

// reveal.js plugins