====== Structural induction ====== ===== Motivation ===== In the last chapter, we have specified two ADTs: $math[List] and $math[FIFO], as well as one $math[FIFO] implementation relying on lists. The following were open issues: * while selecting axioms for $math[append] and $math[reverse], we claimed that they guarantee certain properties of the ADT hold * in showing that the List implementation of the FIFO, we also require a property of lists to hold. In this lecture we will examine **structural induction**: a mechanism which generalises **mathematical induction** and allows us to prove properties of recursively-defined structures such as ADTs. ===== Classification of operators ===== Let the sort $math[T] designate our datatype. Let $math[O : T]. Operators such as $math[O] are called **nullary constructors**: they designate **special/specific** values of the ADT $math[T]. Let $math[E: X \rightarrow T]. Operators such as $math[E] are called **external constructors**: they construct values of $math[T] using **only** elements foreign to the type $math[T] (in our example $math[X]). Let $math[I : T \rightarrow T] or $math[I: X \times T \rightarrow T] or $math[I: T\times T\rightarrow T]. Operators such as $math[I] are called **internal constructors**: they rely on other values of $math[T] (maybe more than one) in order to build a new value of $math[T]. This classification is useful for describing structural induction. ===== Structural induction scheme ===== Suppose we want to prove a property of the form: $math[\forall t \in T : P(t)] If $math[T] would interpret the natural numbers, then a proof by induction implies: * showing the **basis case** (t=0) * showing the **induction step**: * assume P(t) holds (**induction hypothesis**); prove that P(t+1) holds Then, the induction principle guarantees the property to hold for every natural number, since (+1) is the obvious construction step for natural numbers, starting from 0. To prove a property inductively, it is sufficient to **generalise** the induction principle, in order to take into account **all possible construction steps**. Hence: * for each **nullary constructor O**, we need to prove P(O). Each such proof consists in a basis case. * for each **external constructor E** and each value (or sequence of values) e, we need to prove P(E(e)). This is again a basis case * for each **internal constructor I** which takes $math[t_1, \ldots, t_n] as parameter, we need to: * make the **induction hypothesis** $math[P(t_i)] for each $math[i] * prove $math[P(I(t_1,\ldots t_n))] ===== Exercises ===== Prove via structural induction: * $math[size(append(l1,l2)) = size(l1) + size(l2)] * $math[reverse(reverse(l)) = l] * $math[size(l) = size(reverse(l))] * $math[l \neq Void \rightarrow tail(l++l')=tail(l)++l'] ===== Proving implications via structural induction ===== Let us define the $math[sorted] property over lists: $math[sorted:List\rightarrow \mathbb{B}] * $math[sorted(Void) = true] * $math[sorted(e:Void) = true] * $math[sorted(e:e':l) = e < e' \wedge sorted(e':l)] as well as one basic sorting procedure, insertion-sort. We first define: $math[ins:E\times List \rightarrow List] * $math[ins(e,Void) = e:Void] * $math[ins(e',e:l) = e:ins(e',l) \text{ if } e'> e \text{ and } e':e:l \text{ otherwise }] which inserts an element in a sorted list, and produces a sorted list. Then, we have: $math[sort:List \rightarrow List] * $math[sort(Void) = Void] * $math[sort(e:l) = ins(e,sort(l))] ==== Warm-up ==== We can use the $math[sorted] definition in order to prove the correctness of insertion-sort. Before doing so, we start with the proof of a basic property $math[PSort]: $math[sorted(e:l) \implies \forall e'\in l: e\leq e'] First, we formulate the property in the form $math[\forall l. P(l)], that is: $math[\forall l: sorted(e:l) \implies \forall e'\in l: e\leq e'] **Basis case** $math[l = Void]. Since $math[l] contains no elements, the implication trivially holds. **Induction step**. $math[l = x:l']. We prove: $math[sorted(e:x:l') \implies \forall e'\in l': e\leq e']. Suppose $math[sorted(e:x:l')] is true. By the axioms of $math[sorted] we have: * $math[e < x] and * $math[sorted(x:l')] Since $math[sorted(x:l')] is true, by the induction hypothesis we have $math[\forall e'\in l': x\leq e']. Since $math[e < x], it follows that $math[\forall e' \in x:l': e \leq e']. The implication has been proved. ==== Correctness of insertion ==== We continue with the following property $math[PIns], which proves that insertion is correct: $math[\forall l: sorted(l) \rightarrow sorted(ins(e,l))] **Basis case** ($math[l = Void]). First of all, $math[sorted(Void)] is trivially true. Therefore $math[sorted(ins(e,Void))] must also be true, which is indeed the case, as it follows immediately from the axioms. **Induction step**. ($math[l = x:xs]). Suppose $math[sorted(x:xs) (\star)] is true. We show that $math[sorted(ins(e,x:xs))] is also true. **Case 1**: $math[xs = Void]. Then: * $math[sorted(ins(e,x:Void)) = ] * $math[sorted(e:x:Void)] if $math[x > e] or $math[sorted(x:e:Void)], otherwise. In both situations, the axioms for $math[sorted] are verified. **Case 2**: $math[xs \neq Void]. Recall that $math[sorted(x:xs)] is true (the LHS of the implication which we prove), hence: $math[x \leq head(xs)] and $math[sorted(xs)]. From the latter and the induction hypothesis, we have $math[sorted(ins(e,xs)) (\star\star)]. Then: * $math[sorted(ins(e,x:xs)) = ] * **Case 2a:** $math[e > x]: $math[sorted(x:ins(e,xs))]. Then: * from $math[(\star)] and $math[PSort], we have $math[\forall y\in (x:xs): x \leq y] - x is the smallest element of $math[x:xs]; * but $math[e > x], hence $math[x] is at least as small as any element of $math[ins(e,xs)]. In particular, $math[x \leq head(ins(e,xs))] (which is well-defined since $math[xs\neq Void]). * from $math[(\star\star)] we have $math[sorted(ins(e,xs))] which concludes the proof for $math[sorted(x:ins(e,xs))]. * **Case 2b:** $math[e \leq x]: $math[sorted(e:x:xs)]. Since $math[e \leq x] is true and $math[sorted(x:xs) (\star)], the claim is true. This concludes the proof of our statement. ==== Correctness of InsertionSort ==== The correctness of InsertionSort is expressed by: $math[\forall l: sorted(sort(l))] **Basis case** $math[l = Void]. The proposition follows immediately from the axioms. **Induction step**: $math[l = x:xs]. The induction hypothesis is $math[sorted(sort(xs))]. We need to prove: * $math[sorted(sort(x:xs))=] * $math[sorted(ins(x,sort(xs))]. Since $math[sorted(sort(xs))] (induction hypothesis) from $math[PIns] the statement follows immediately.