module Types.equational2 where
open import Agda.Primitive using (Level; _⊔_; lsuc; lzero)
open import Types.relations
open import Types.equality
open import Types.product using (Σ; _,_)
We now define a more complex version of equational reasoning on top of an equivalence relation _~_
rather than on equality.
module ★-reasoning
{a ℓ}
{A : Set a}
(_∼_ : Rel A ℓ)
(reflexive : Reflexive _∼_)
(trans : Transitive _∼_)
where
infix 4 _IsRelatedTo_
infix 3 _∎
infixr 2 _∼⟨_⟩_ _≡⟨_⟩_ _≡˘⟨_⟩_ _≡⟨⟩_
infix 1 begin_
This seemingly unnecessary type is used to make it possible to infer arguments even if the underlying equality evaluates.
data _IsRelatedTo_ (x y : A) : Set (a ⊔ ℓ) where
: (x∼y : x ∼ y) → x IsRelatedTo y relTo
Use this to indicate beginning of reasoning:
_ : ∀ {x y} → x IsRelatedTo y → x ∼ y
begin= x∼y begin relTo x∼y
chaining transitivity:
_∼⟨_⟩_ : ∀ x {y z} → x ∼ y → y IsRelatedTo z → x IsRelatedTo z
_ ∼⟨ x∼y ⟩ relTo y∼z = relTo (trans x∼y y∼z)
covariant equivalence preservation:
_≡⟨_⟩_ : ∀ x {y z} → x ≡ y → y IsRelatedTo z → x IsRelatedTo z
_ ≡⟨ refl ⟩ x∼z = x∼z
contravariant equivalence preservation:
_≡˘⟨_⟩_ : ∀ x {y z} → y ≡ x → y IsRelatedTo z → x IsRelatedTo z
_ ≡˘⟨ refl ⟩ x∼z = x∼z
preservation of reflexivity:
_≡⟨⟩_ : ∀ x {y} → x IsRelatedTo y → x IsRelatedTo y
_ ≡⟨⟩ x∼y = _ ≡⟨ refl ⟩ x∼y
and we end chains of reasoning with a QED:
_∎ : ∀ x → x IsRelatedTo x
_∎ _ = relTo reflexive
A lot of asymmetric laws can be derived with one half of the symmetry and mixing it with commutativity. E.g. left inverse could be derived using right inverse and commutativity, similarly, right inverse can be derived using left inverse and commutativity.
open import Types.operations
module withCommutativity {a ℓ}
{A : Set a}
{_∼_ : Rel A ℓ}
{_•_ : (★ _∼_) A}
{_⁻¹ : (♠ _∼_) A}
(reflexive : Reflexive _∼_)
(trans : Transitive _∼_)
(comm : Commutative _∼_ _•_)
(ϵ : A)
where
open ★-reasoning _∼_ reflexive trans public
:
comm+invˡ⇒invʳ _∼_ ϵ _⁻¹ _•_
LeftInverse → RightInverse _∼_ ϵ _⁻¹ _•_
= begin
comm+invˡ⇒invʳ invˡ x (x ⁻¹) ∼⟨ comm x (x ⁻¹) ⟩
x • (x ⁻¹) • x ∼⟨ invˡ x ⟩
ϵ ∎
: RightInverse _∼_ ϵ _⁻¹ _•_ → LeftInverse _∼_ ϵ _⁻¹ _•_
comm+invʳ⇒invˡ = begin
comm+invʳ⇒invˡ invʳ x (x ⁻¹) • x ∼⟨ comm (x ⁻¹) x ⟩
(x ⁻¹) ∼⟨ invʳ x ⟩
x • ϵ ∎
module withCongruence {a ℓ}
{A : Set a}
(_∼_ : Rel A ℓ)
(_•_ : (★ _∼_) A)
(_⁻¹ : (♠ _∼_) A)
(reflexive : Reflexive _∼_)
(trans : Transitive _∼_)
(sym : Symmetric _∼_)
(cong : Congruent₂ _∼_ _•_)
(ϵ : A)
where
open ★-reasoning _∼_ reflexive trans public
Given an operation is associative, has an identity, every given right inverse has a unique left inverse.
:
assoc+id+invʳ⇒invˡ-unique _∼_ _•_
Associative → Identity _∼_ ϵ _•_
→ RightInverse _∼_ ϵ _⁻¹ _•_
→ ∀ x y
→ (x • y) ∼ ϵ
→ x ∼ (y ⁻¹)
(idˡ , idʳ) invʳ x y eq = begin
assoc+id+invʳ⇒invˡ-unique assoc (idʳ x) ⟩
x ∼⟨ sym (sym (invʳ y)) ⟩
x • ϵ ∼⟨ cong reflexive (y • (y ⁻¹)) ∼⟨ sym (assoc x y (y ⁻¹)) ⟩
x • (x • y) • (y ⁻¹) ∼⟨ cong eq reflexive ⟩
(y ⁻¹) ∼⟨ idˡ (y ⁻¹) ⟩
ϵ • y ⁻¹ ∎
Given an operation is associative, has an identity, every given left inverse has a unique right inverse.
:
assoc+id+invˡ⇒invʳ-unique _∼_ _•_
Associative → Identity _∼_ ϵ _•_
→ LeftInverse _∼_ ϵ _⁻¹ _•_
→ ∀ x y
→ (x • y) ∼ ϵ
→ y ∼ (x ⁻¹)
(idˡ , idʳ) invˡ x y eq = begin
assoc+id+invˡ⇒invʳ-unique assoc (idˡ y) ⟩
y ∼⟨ sym (sym (invˡ x)) reflexive ⟩
ϵ • y ∼⟨ cong ((x ⁻¹) • x) • y ∼⟨ assoc (x ⁻¹) x y ⟩
(x ⁻¹) • (x • y) ∼⟨ cong reflexive eq ⟩
(x ⁻¹) • ϵ ∼⟨ idʳ (x ⁻¹) ⟩
x ⁻¹ ∎
Equational reasoning can also be done on other relations except equality and equivalence ones. For example, here we
derive the framework for the order operator _<=_
:
open import Types.proofsAsData using (_<=_; ltz; lt)
open import Lang.dataStructures
open import Types.relations hiding (Rel)
: Transitive {A = ℕ} _<=_
≤-trans = ltz
≤-trans ltz j≤k (lt x) (lt y) = lt (≤-trans x y)
≤-trans
module ≤-Reasoning where
infix 3 _■
infixr 2 _≤⧏⧐_ _≤⧏_⧐_
infix 1 begin_
_ : ∀ {x y : ℕ} → x <= y → x <= y
begin_ x≤y = x≤y
begin
-- Apply reflexivity, arguments required within the ⧏⧐
_≤⧏⧐_ : ∀ (x {y} : ℕ) → x <= y → x <= y
_ ≤⧏⧐ x≤y = x≤y
-- Transitivity with arguments applied within the ⧏⧐
_≤⧏_⧐_ : ∀ (x {y z} : ℕ) → x <= y → y <= z → x <= z
_ ≤⧏ x≤y ⧐ y≤z = ≤-trans x≤y y≤z
_■ : ∀ (x : ℕ) → x <= x
_■ zero = ltz
_■ (succ x) = lt (_■ x)
As we see the pattern above, given the proof for transitivity of an operator, we can generate the constructs for doing equational with the operator.
module λ-Reasoning {a ℓ}
{A : Set a}
{_⌬_ : Rel A ℓ}
{⌬-trans : Transitive _⌬_}
{⌬-refl : Reflexive _⌬_}
where
infix 3 _▐
infixr 2 _⌬◀▶_ _⌬◀_▶_
infix 1 begin_
_ : ∀ {x y : A} → x ⌬ y → x ⌬ y
begin_ x⌬y = x⌬y
begin
-- Apply reflexivity, arguments required within the ◀▶
_⌬◀▶_ : ∀ (x {y} : A) → x ⌬ y → x ⌬ y
_ ⌬◀▶ x⌬y = x⌬y
-- Transitivity with arguments applied within the ◀▶
_⌬◀_▶_ : ∀ (x {y z} : A) → x ⌬ y → y ⌬ z → x ⌬ z
_ ⌬◀ x⌬y ▶ y⌬z = ⌬-trans x⌬y y⌬z
_▐ : ∀ (x : A) → x ⌬ x
_▐ _ = ⌬-refl