Formal Software Verification

5.7. Worked Examples🔗

Each example below turns a Lecture 4 artefact around into the forward, structured style, so the two lectures read as one argument seen from both ends. Lean checks every line when the notes are built.

5.7.1. The distributive law, forwards🔗

The statement a ∧ (b ∨ c) → (a ∧ b) ∨ (a ∧ c) was the first worked example of Lecture 4, proved backwards. Forwards it reads as a structured term. From the hypothesis we have a and we have b ∨ c; from b ∨ c we get two cases; in each we build the matching disjunct with the anonymous constructor.

namespace Forward theorem and_or_distrib (a b c : Prop) : a (b c) (a b) (a c) := assume habc : a (b c); have ha : a := And.left habc; Or.elim (And.right habc) (fun hb => Or.inl (And.intro ha hb)) (fun hc => Or.inr (And.intro ha hc)) end Forward

In words. Assume a ∧ (b ∨ c), and name its left conjunct ha. Its right conjunct is a disjunction, so we reason by cases. If b holds, the left disjunct a ∧ b follows from ha and b. If c holds, the right disjunct a ∧ c follows from ha and c. The backward proof of Lecture 4 applied Or.elim to split the goal and closed each branch with bullets; the forward proof consumes the same disjunction with Or.elim and returns the built disjunct directly. The trade-off is the usual one, the backward script planning from the goal and the forward term building from the hypotheses.

5.7.2. Forall_one_point in full🔗

The one-point rule (∀ x, x = t → P x) ↔ P t is the payoff of the connectives section, and it is a quantifier proof that is natural forwards and awkward backwards.

namespace Forward theorem Forall_one_point_worked (α : Type) (t : α) (P : α Prop) : ( x, x = t P x) P t := Iff.intro (assume h : x, x = t P x; h t rfl) (assume hpt : P t; fix x : α; assume hxt : x = t; hxt hpt) end Forward

The forward direction instantiates the hypothesis h at the fixed value t and discharges the guard t = t with rfl, so h t rfl proves P t. The backward direction fixes an arbitrary x, assumes the guard x = t, and rewrites P t into P x with the substitution hxt ▸ hpt, where hxt : x = t carries the equation. Backwards the same proof would leave a metavariable for the witness and an awkward equation to discharge; forwards the witness is simply t.

5.7.3. A calculational proof🔗

The identity 2 * m + n = m + n + m has three proofs, and comparing them draws the moral of the calculational style.

namespace Forward theorem two_mul_example_calc (m n : ) : 2 * m + n = m + n + m := m:n:2 * m + n = m + n + m calc 2 * m + n = (m + m) + n := m:n:2 * m + n = m + m + n All goals completed! 🐙 _ = m + n + m := m:n:m + m + n = m + n + m All goals completed! 🐙 theorem two_mul_example_trans (m n : ) : 2 * m + n = m + n + m := m:n:2 * m + n = m + n + m m:n:h1:2 * m + n = m + m + n2 * m + n = m + n + m m:n:h1:2 * m + n = m + m + nh2:m + m + n = m + n + m2 * m + n = m + n + m All goals completed! 🐙 theorem two_mul_example_ac (m n : ) : 2 * m + n = m + n + m := m:n:2 * m + n = m + n + m m:n:m + m + n = m + n + m All goals completed! 🐙 end Forward

The calc proof documents the chain that the reader follows. The Eq.trans proof shows the transitivity that calc hides. The ac_rfl proof hides the chain altogether and lets the checker rearrange the terms. All three are correct and rest on the same facts; the choice is about the reader, not the checker.

5.7.4. reverse_reverse by recursion🔗

Reversing a list twice returns the list, and the proof recurses on the list, using the reverse_append proved above as its auxiliary. This closes the loop with the fourth worked example of Lecture 4, which discharged reverse_cons.

namespace Forward theorem reverse_reverse {α : Type} : (xs : List α), reverse (reverse xs) = xs | [] => rfl α:Typex:αxs:List αreverse (reverse (x :: xs)) = x :: xs α:Typex:αxs:List αreverse (reverse (x :: xs)) = x :: xs All goals completed! 🐙 end Forward

The base case reverses the empty list twice and closes by rfl. In the step case, reversing x :: xs gives appendPretty (reverse xs) [x], and reversing that, by reverse_append, brings the head back to the front and leaves reverse (reverse xs), which the recursive call, the induction hypothesis, rewrites to xs. The induction tactic of Lecture 4 would prove the same statement with ih in place of the recursive call; the two are the same proof. Weeks 6 and 7 give the general method for arbitrary inductive types.