{-# OPTIONS --without-K --exact-split --rewriting #-}
open import BasicTypes public
open import BasicFunctions public

Algebra of paths

ap
  :  {ℓ₁ ℓ₂ : Level} {A : Type ℓ₁}{B : Type ℓ₂}
   (f : A  B) {a₁ a₂ : A}
   a₁ == a₂
  --------------
   f a₁ == f a₂

ap f idp = idp

More syntax:

cong  = ap
app-≡ = ap

syntax app-≡ f p = f [[ p ]]

Now, we can define a convenient syntax sugar for ap in equational reasoning.

infixl 40 ap
syntax ap f p = p |in-ctx f

Let’s suppose we have a lemma:

lemma : a ≡ b
lemma = _

used in an equational reasoning like:

t : a ≡ e
t = f a ≡⟨ ap f lemma ⟩
    f b
    ∎

Then, we can now put the lemma in front:

t : a == e
t = f a =⟨ lemma |in-ctx f ⟩
    f b
    ∎

Lastly, we can also define actions on two paths:

ap²
  :  {ℓ₁ ℓ₂ ℓ₃ : Level} {A : Type ℓ₁}{B : Type ℓ₂} {C : Type ℓ₃}  {a₁ a₂ : A} {b₁ b₂ : B}
   (f : A  B  C)
   (a₁ == a₂)  (b₁ == b₂)
  --------------------------
   f a₁ b₁  == f a₂ b₂

ap² f idp idp = idp
ap-·
  :  {ℓ₁ ℓ₂ : Level} {A : Type ℓ₁}{B : Type ℓ₂} {a b c : A}
   (f : A  B)  (p : a == b)  (q : b == c)
  -------------------------------------------
   ap f (p · q) == ap f p · ap f q

ap-· f idp q = refl (ap f q)
ap-inv
  :  {ℓ₁ ℓ₂ : Level} {A : Type ℓ₁}{B : Type ℓ₂}  {a b : A}
   (f : A  B)  (p : a == b)
  ----------------------------
   ap f (p ⁻¹) == (ap f p) ⁻¹

ap-inv f idp = idp

More syntax:

ap-! = ap-inv
ap-comp
  :  {ℓ₁ ℓ₂ ℓ₃ : Level} {A : Type ℓ₁}{B : Type ℓ₂} {C : Type ℓ₃} {a b : A}
   (f : A  B)
   (g : B  C)
   (p : a == b)
  -------------------------------
   ap g (ap f p) == ap (g ∘ f) p

ap-comp f g idp = idp
ap-id
  :  {: Level} {A : Type ℓ} {a b : A}
   (p : a == b)
  --------------
   ap id p == p

ap-id idp = idp
ap-const
  :  {ℓ₁ ℓ₂ : Level} {A : Type ℓ₁}{B : Type ℓ₂}  {a a' : A} {b : B}
   (p : a == a')
  -----------------------
   ap (λ _  b) p == idp

ap-const {b = b} idp = refl (refl b)

Properties on the groupoid

Some properties on the groupoid structure of equalities

·-runit
  :  {: Level} {A : Type ℓ}  {a a' : A}
   (p : a == a')
  --------------
   p == p · idp

·-runit idp = idp

For convenience, we add the following rewriting rule.

open import Rewriting

postulate
  runit
    :  {: Level} {A : Type ℓ}  {a a' : A}
     {p : a == a'}
    --------------
     p · idp ↦ p

{-# REWRITE runit #-}
·-lunit
  :  {: Level} {A : Type ℓ}  {a a' : A}
   (p : a == a')
  --------------
   p == idp · p

·-lunit idp = idp
·-linv
  :  {: Level} {A : Type ℓ}  {a a' : A}
   (p : a == a')
  ----------------
   ! p · p == idp

·-linv idp = idp

≡-inverse-left = ·-linv
·-rinv
  :  {: Level} {A : Type ℓ}  {a a' : A}
   (p : a == a')
  ----------------
   p · ! p == idp

·-rinv idp = idp

≡-inverse-right  = ·-rinv
involution
  :  {: Level} {A : Type ℓ}  {a a' : A}
   (p : a == a')
  ---------------
   ! (! p) == p

involution idp = idp
·-assoc
  :  {: Level} {A : Type ℓ} {a b c d : A}
   (p : a == b)  (q : b == c)  (r : c == d)
  --------------------------------------------
   p · q · r == p · (q · r)

·-assoc idp q r = idp
·-cancellation
  :  {: Level} {A : Type ℓ} {a : A}
   (p : a == a)  (q : a == a)
   p · q == p
  -----------------------------------------
   q == refl a

·-cancellation {a = a} p q α =
    begin
      q               ==⟨ ap (_· q) (! (·-linv p))(! p · p) · q   ==(·-assoc (! p) p q) ⟩
      ! p · (p · q)   ==⟨ ap (! p ·_) α ⟩
      ! p · p         ==⟨ ·-linv p ⟩
      refl a
    ∎

Moving a term from one side to the other is a common task, so let’s define some handy functions for that.

·-left-to-right-l
  :  {: Level} {A : Type ℓ} {a b c : A} {p : a == b} {q : b == c} {r : a == c}
   p · q == r
  ------------------
       q == ! p · r

·-left-to-right-l {a = a}{b = b}{c = c} {p} {q} {r} α =
  begin
    q
      ==⟨ ·-lunit q ⟩
    refl b · q
      ==⟨ ap (_· q) (! (·-linv p))(! p · p) · q
      ==⟨ ·-assoc (! p) p q ⟩
    ! p · (p · q)
      ==⟨ ap (! p ·_) α ⟩
    ! p · r
  ∎
·-left-to-right-r
  :  {: Level} {A : Type ℓ} {a b c : A} {p : a == b} {q : b == c} {r : a == c}
   p · q == r
  -------------------
        p == r · ! q

·-left-to-right-r {a = a}{b = b}{c = c} {p} {q} {r} α =
  begin
    p
      ==⟨ ·-runit p ⟩
    p · refl b
      ==⟨ ap (p ·_) (! (·-rinv q)) ⟩
    p · (q · ! q)
      ==⟨ ! (·-assoc p q (! q))(p · q) · ! q
      ==⟨ ap (_· ! q) α ⟩
    r · ! q
  ∎
·-right-to-left-r
  :  {: Level} {A : Type ℓ} {a b c : A} {p : a == c} {q : a == b} {r : b == c}
         p == q · r
  -------------------
   p · ! r == q

·-right-to-left-r {a = a}{b = b}{c = c} {p} {q} {r} α =
  begin
    p · ! r
      ==⟨ ap (_· ! r) α ⟩
    (q · r) · ! r
      ==⟨ ·-assoc q r (! r) ⟩
    q · (r · ! r)
      ==⟨ ap (q ·_) (·-rinv r) ⟩
    q · refl b
      ==⟨ ! (·-runit q) ⟩
    q
    ∎
·-right-to-left-l
  :  {: Level} {A : Type ℓ} {a b c : A} {p : a == c} {q : a == b} {r : b == c}
         p == q · r
  ------------------
   ! q · p == r

·-right-to-left-l {a = a}{b = b}{c = c} {p} {q} {r} α =
  begin
    ! q · p
      ==⟨ ap (! q ·_) α ⟩
    ! q · (q · r)
      ==⟨ ! (·-assoc (! q) q r) ⟩
    ! q · q · r
      ==⟨ ap (_· r) (·-linv q) ⟩
    refl b · r
      ==⟨ ! (·-lunit r) ⟩
    r
  ∎

Finally, when we invert a path composition this is what we got.

!-·
  :  {: Level} {A : Type ℓ} {a b : A}
   (p : a == b)
   (q : b == a)
  --------------------------
   ! (p · q) == ! q · ! p

!-· idp q = ·-runit (! q)