Formal Software Verification

5.3. Forward Reasoning about Connectives and Quantifiers🔗

Lecture 4 applied the rules of the connectives backwards with apply. Forwards, the same rules are used by juxtaposition, supplying the hypothesis directly. An elimination rule takes a hypothesis apart, and an introduction rule builds the goal. Thus And.left h and And.right h extract the two conjuncts, And.intro ha hb and the anonymous constructor ⟨ha, hb⟩ build a conjunction, Or.inl and Or.inr build a disjunction, Or.elim h f g consumes one with two function branches, Iff.mp and Iff.mpr apply an equivalence in each direction, Exists.intro t pf supplies a witness, and Exists.elim h f names the witness of an existential hypothesis. Each is a forward step, and a structured proof strings them together with have.

The commutativity of conjunction, proved backwards in Lecture 4, reads forwards as three have steps.

namespace Forward theorem And_swap (a b : Prop) : a b b a := assume h : a b; have ha : a := And.left h; have hb : b := And.right h; show b a from And.intro hb ha end Forward

The commutativity of disjunction consumes the hypothesis with Or.elim and rebuilds it on the other side. modus_ponens and Not_Not_intro combine the steps seen so far, recalling that ¬ a is a → False.

namespace Forward theorem Or_swap (a b : Prop) : a b b a := assume h : a b; Or.elim h (fun ha => Or.inr ha) (fun hb => Or.inl hb) theorem modus_ponens (a b : Prop) : (a b) a b := assume hab : a b; assume ha : a; show b from hab ha theorem Not_Not_intro (a : Prop) : a ¬¬ a := assume ha : a; assume hna : ¬ a; show False from hna ha end Forward

The high point of the section is the pair of one-point rules, which collapse a quantifier whose bound variable is pinned to a fixed value by an equation. The rule for ∀ says that a universally quantified implication guarded by x = t is equivalent to its instance at t; the rule for ∃ is its existential mirror. Each proof is structured, and each is more natural forwards than backwards.

namespace Forward theorem Forall_one_point (α : 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) theorem Exists_one_point (α : Type) (t : α) (P : α Prop) : ( x, x = t P x) P t := Iff.intro (assume h : x, x = t P x; Exists.elim h (fun x hx => hx.1 hx.2)) (assume hpt : P t; Exists.intro t (And.intro rfl hpt)) end Forward

In the forward direction of the ∀ rule, the hypothesis is instantiated at t and the guard t = t is discharged by rfl. In the backward direction, an arbitrary x is fixed, the guard x = t is assumed, and the equation rewrites P t into P x through the substitution operator . The ∃ rule supplies the witness t on one side and names the witness on the other.

5.3.1. Examples🔗

The examples below apply each rule forwards by juxtaposition, then prove the two one-point rules.

Example 1. And.left and And.right extract the two conjuncts forwards.

namespace Forward example (a b : Prop) (h : a b) : a := And.left h example (a b : Prop) (h : a b) : b := And.right h end Forward

Example 2. And.intro and the anonymous constructor build a conjunction, and the two terms are the same.

namespace Forward example (a b : Prop) (ha : a) (hb : b) : a b := And.intro ha hb example (a b : Prop) (ha : a) (hb : b) : a b := ha, hb end Forward

Example 3. The commutativity of conjunction forwards, beside its Lecture 4 backward script.

namespace Forward example (a b : Prop) : a b b a := assume h : a b; And.intro (And.right h) (And.left h) example (a b : Prop) : a b b a := a:Propb:Propa b b a a:Propb:Proph:a bb a a:Propb:Proph:a bba:Propb:Proph:a ba a:Propb:Proph:a bb All goals completed! 🐙 a:Propb:Proph:a ba All goals completed! 🐙 end Forward

Example 4. Or.inl and Or.inr build a disjunction by choosing a side.

namespace Forward example (a b : Prop) (ha : a) : a b := Or.inl ha example (a b : Prop) (hb : b) : a b := Or.inr hb end Forward

Example 5. Or.elim h f g consumes a disjunction with two function branches.

namespace Forward example (a b c : Prop) (h : a b) (f : a c) (g : b c) : c := Or.elim h f g end Forward

Example 6. Iff.mp and Iff.mpr apply an equivalence in each direction by juxtaposition.

namespace Forward example (a b : Prop) (h : a b) (ha : a) : b := Iff.mp h ha example (a b : Prop) (h : a b) (hb : b) : a := Iff.mpr h hb end Forward

Example 7. Exists.intro t pf supplies a witness forwards, and the anonymous constructor is the same term.

namespace Forward example (P : Prop) (h : P 3) : n, P n := Exists.intro 3 h example (P : Prop) (h : P 3) : n, P n := 3, h end Forward

Example 8. Exists.elim h f names the witness of an existential hypothesis in a function branch.

namespace Forward example (α : Type) (P : α Prop) (Q : Prop) (h : x, P x) (f : x, P x Q) : Q := Exists.elim h f end Forward

Example 9. The one-point rule for ∀ forwards, instantiating at t on one side and rewriting with the guard on the other.

namespace Forward example (α : 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

Example 10. The one-point rule for ∃ forwards, contrasting the witness supplied on one side with the witness named on the other.

namespace Forward example (α : Type) (t : α) (P : α Prop) : ( x, x = t P x) P t := Iff.intro (assume h : x, x = t P x; Exists.elim h (fun x hx => hx.1 hx.2)) (assume hpt : P t; Exists.intro t (And.intro rfl hpt)) end Forward