📥 Download raw
module Padova2025.ProvingBasics.Equality.Reasoning where

Equational reasoning

open import Padova2025.ProgrammingBasics.Naturals.Base
open import Padova2025.ProgrammingBasics.Naturals.Arithmetic
open import Padova2025.ProvingBasics.Equality.Base
open import Padova2025.ProvingBasics.Equality.General
open import Padova2025.ProvingBasics.Equality.NaturalNumbers

The equality proofs we have discussed before have been put in so-called combinatorial style, making use of the combinators trans, sym and and cong. While proofs in this style are not too unreasonable to construct, if one makes extensive use of Agda’s assistive features, the resulting proofs are often hard to read. Such “write-only” proofs constitute an antithesis to the ideals of elegance we are striving for when playing Agda. They also look totally unlike usual blackboard proofs of identities.

Instead, on a blackboard, we usually verify identities by chains of equalities, using trans implicitly rather than explicitly. This style is called equational reasoning, and it is also available in Agda. For instance, here is how the proof of commutativity of addition looks like in this style.

open import Padova2025.ProvingBasics.Equality.Reasoning.Core
-- not magic, but nevertheless recommended to skip on first reading

+-comm' : (a b : )  a + b  b + a
+-comm' zero     b = sym (+-zero b)
+-comm' (succ a) b = begin
  succ a + b   ≡⟨⟩
  succ (a + b) ≡⟨ cong succ (+-comm' a b) 
  succ (b + a) ≡⟨ sym (+-succ b a) 
  b + succ a   

-- compare with the combinator-style proof:
-- +-comm (succ a) b =
--   trans (cong succ (+-comm a b)) (sym (+-succ b a))

We start with the left-hand side and then repeatedly carry out manipulations until we arrive at the right-hand side. Steps indicated by ≡⟨⟩ hold by definition (by refl). Steps indicated by ≡⟨ p ⟩ hold by the specified reason (element of the corresponding identity type) p.

Instead of ≡⟨ sym p ⟩, we can also write ≡˘⟨ p ⟩.

Exercise: Distributivity, revisited

Redo the earlier proof of distributivity in equational style.

·-distribʳ-+' : (a b c : )  (a + b) · c  a · c + b · c
·-distribʳ-+' = {!!}

Exercise: Commutativity of multiplication

·-zero : (a : )  a · zero  zero
·-zero = {!!}
+-swap : (a b c : )  a + (b + c)  b + (a + c)
+-swap = {!!}
·-succ : (a b : )  a · succ b  a + a · b
·-succ = {!!}
·-comm : (a b : )  a · b  b · a
·-comm = {!!}

As a corollary, from commutativity and the first distributive law we obtain the other distributive law.

·-distribˡ-+ : (a b c : )  a · (b + c)  a · b + a · c
·-distribˡ-+ = {!!}

Exercise: Binomial theorem

binomial-theorem : (x y : )  (x + y) ²  x ² + two · (x · y) + y ²
binomial-theorem = {!!}

It is instructive to do this proof, once, by hand, so as to appreciate the nontriviality of the binomial theorem if the only tools available are the axioms and their basic consequences. There is a ring solver which can generate the proof automatically.

Exercise: Summing with and without an accumulator

open import Padova2025.ProgrammingBasics.Lists

Back when we have introduced lists, we have implemented a sum function which sums the elements of a given list of natural numbers:

-- sum : List ℕ → ℕ
-- sum []       = zero
-- sum (x ∷ xs) = x + sum xs

An alternative, tail-recursive, implementation makes use of an accumulator:

sumAcc :   List   
sumAcc acc []       = acc
sumAcc acc (x  xs) = sumAcc (acc + x) xs

sum' : List   
sum' = sumAcc zero

Let us prove that these two implementations yield identical results.

lemma-sumAcc : (acc : ) (xs : List )  sumAcc acc xs  acc + sum xs
lemma-sumAcc = {!!}
sum-sum' : (xs : List )  sum xs  sum' xs
sum-sum' = {!!}

Exercise: Reversing with and without an accumulator

Similarly to the previous exercise—there are two natural implementations of the reverse function on lists, not using or using an accumulator:

-- Without accumulator.
-- reverse : {A : Set} → List A → List A
-- reverse []       = []
-- reverse (x ∷ xs) = reverse xs ∷ʳ x

-- With accumulator.
reverseAcc : {A : Set}  List A  List A  List A
reverseAcc acc []       = acc
reverseAcc acc (x  xs) = reverseAcc (x  acc) xs

reverse' : {A : Set}  List A  List A
reverse' = reverseAcc []

For the following proof, the function snoc-++ is useful.

open import Padova2025.ProvingBasics.Equality.Lists
lemma-reverseAcc : {A : Set} (acc : List A) (xs : List A)  reverseAcc acc xs  reverse xs ++ acc
lemma-reverseAcc = {!!}
reverse-reverse' : {A : Set} (xs : List A)  reverse xs  reverse' xs
reverse-reverse' = {!!}