Contents

  1. Pointed Types
  2. Loop Spaces

{-# OPTIONS --without-K --safe #-}

module pointed where

open import mltt
open import twocatconstructions
open import naturals using () renaming (suc to S)
open import negation using ( _⟺_ )

open ≡-Reasoning
open ◾-lemmas
open ap-lemmas

Pointed Types

Let us begin by defining the type of pointed types (note that it belongs to a higher universe). Again, we use a record so we can name the fields. Note also that Agda allows to overload the constructors. We reuse _,_ for pointed types. Indeed, an alternative definition would have been

module PointedTypes where

  record Ptd ( : Level) : Set (lsuc ) where
    constructor _,_
    field
      U⊙ : Set  --forget 
      pt : U⊙

  open Ptd

By “opening” the record (module, really) we make the two names U⊙ and pt visible without the prefix Ptd; that is we can write U⊙ instead of Ptd.U⊙.

Next, for any type and term in it, we get a pointed type:
  
  ptd :  {} (A : Set )  A  Ptd 
  ptd {} A x = A , x

Another familiar operation in Topology is to add a disjoint point to make a space pointed. We do the same here:

  ⊙F : ∀{} (A : Set )  Ptd 
  U⊙ (⊙F A) = 𝟙 + A
  pt (⊙F A) = inl *
  pattern + = inl * -- base point

It is conventional to denote the disjoint base point as \(+\), so we have added a pattern to achieve that.

Morphisms of pointed spaces are the obvious thing: a function respecting the base point. Here, we define it as a pair consisting of a term f : A → B and an identification of base points in f x ≡ y:

  infix 10 _⊙→_
  _⊙→_ : ∀{ ℓ'}  Ptd   Ptd ℓ'  Set (  ℓ')
  (A , x) ⊙→ (B , y) = Σ[ f  (A  B) ] (f x  y )

The identity function, explicit and implicit versions:

  -- explicit version
  ⊙id : ∀{} (X : Ptd )  X ⊙→ X
  Σ.fst (⊙id X) = id
  Σ.snd (⊙id X) = idp (pt X)

  -- implicit version
  ⊙id′ : ∀{} {X : Ptd }  X ⊙→ X
  ⊙id′ {X = X} = ⊙id X

Since the types are pointed, we can define the constant morphism collapsing everything to the target’s base point

  ⊙cst : ∀{ ℓ'} {X : Ptd } {Y : Ptd ℓ'}  X ⊙→ Y
  ⊙cst {Y = Y} =  x  pt Y) , idp (pt Y)

The type 𝟙 is naturally pointed; we denote it by ⊙𝟙:

  ⊙𝟙 : Ptd 0ℓ
  U⊙ ⊙𝟙 = 𝟙
  pt ⊙𝟙 = *

It acts as a zero object among pointed types. There is a morphism to any pointed type

  ⊙𝟙⊙→ : ∀{} (X : Ptd )  ⊙𝟙 ⊙→ X
  Σ.fst (⊙𝟙⊙→ X) = 𝟙-rec (pt X)
  Σ.snd (⊙𝟙⊙→ X) = idp (pt X)

and of course a morphism from any pointed type:

  ⊙→⊙𝟙 : ∀{} (X : Ptd )  X ⊙→ ⊙𝟙
  ⊙→⊙𝟙 X = ⊙cst {X = X}

There is of course the composition of pointed maps:

  infix 2 _⊙∘_
  _⊙∘_ : ∀{ ℓ' ℓ''} {X : Ptd } {Y : Ptd ℓ'} {Z : Ptd ℓ''}  
         Y ⊙→ Z  X ⊙→ Y  X ⊙→ Z
  Σ.fst ((g , q) ⊙∘ (f , p)) = g ∘′ f
  Σ.snd ((g , q) ⊙∘ (f , p)) = (ap g p)  q

top ⇑


Loop Spaces

module LoopSpaces { : Level} where
  
  open PointedTypes

First, let us define a generic loop functor. Analogously to the same situation in Topology, we consider the based loop functor, as opposed to the free one, hence we define it in the context of pointed types. Thus, given a pointed type (U⊙ , pt), its loop “space” is defined as the type of identifications of its base point, pointed by the reflexivity identificaction of said base point:

  ⊙Ω : Ptd   Ptd 
  ⊙Ω (U⊙ , pt) = (pt  pt) , (idp pt)

with the underlying carrier type (without the base point):

  Ω⊙ : Ptd   Set  
  Ω⊙ = Ptd.U⊙ ∘′ ⊙Ω

The loop construction can of course be iterated:

  ⊙Ω^ : (n : )  Ptd   Ptd 
  ⊙Ω^ 0 X = X
  ⊙Ω^ (S n) X = ⊙Ω (⊙Ω^ n X)

which is to say that the \(n^\mathrm{th}\) loop space is the loop space of the one at level \(n-1\). This definition can be tweaked to make it look more like the recursive ones we are using in Type Theory:

  -- Definition in HoTT book 
  ⊙Ω′^ : (n : )  Ptd   Ptd 
  ⊙Ω′^ 0 X = X
  ⊙Ω′^ (S n) X = ⊙Ω^ n (⊙Ω X)

Now we introduce a more ordinary version of the loop functor where we explicitly mark the base point: given a type and a term a : A, Ω A a is the loop space based at a : A. Note that this is an ordinary type, not a pointed one.

  Ω : (A : Set )  A  Set 
  Ω A a = Ω⊙ (ptd A a)

We iterate it one more time. For the double loop space we do not need to specify the base point, as the previous iteration comes naturally pointed.

  Ω² : (A : Set )  A  Set 
  Ω² A a = Ω (Ω A a) (idp a) --explicitly mark the identity path at the base point
  module Eckmann-Hilton {A : Set } {a : A} where

         lemma[i] : (α β : Ω² A a)  (α ◾ʳ idp a)  (idp a ◾ˡ β)  α  β
         lemma[i] α β = (α ◾ʳ idp a)  (idp a ◾ˡ β) ≡⟨ (◾ʳrefl α)  (refl◾ˡ β) 
                        (α  refl)  (β  refl)     ≡⟨ ru α  ru β 
                        α  β 

         lemma[ii] : (α β : Ω² A a)  (idp a ◾ˡ β)  (α ◾ʳ idp a)  β  α
         lemma[ii] α β = (idp a ◾ˡ β)  (α ◾ʳ idp a) ≡⟨ (refl◾ˡ β)  (◾ʳrefl α) 
                         (β  refl)  (α  refl)     ≡⟨ ru β  ru α 
                         β  α 

         Thm[EH] : (α β : Ω² A a)  α  β  β  α
         Thm[EH] α β = α  β                       ≡⟨ ★-is-★′ α β 
                       α ★′ β                      ≡⟨ refl 
                       (idp a ◾ˡ β)  (α ◾ʳ idp a) ≡⟨ lemma[ii] α β 
                       β  α ≡⟨ (lemma[i] β α) ⁻¹ 
                       (β ◾ʳ idp a)  (idp a ◾ˡ α) ≡⟨ refl 
                       β  α 

         Corollary : (α β : Ω² A a)  α  β  β  α
         Corollary α β = α  β ≡⟨ (lemma[i] α β) ⁻¹ 
                         α  β ≡⟨ Thm[EH] α β 
                         β  α ≡⟨ lemma[i] β α 
                         β  α 

top ⇑