okay so I've been going down this rabbit hole (heh) for the past week and I need to talk about one of my absolute favourite results in all of mathematics: the synthetic proof that π₁(S¹) ≃ ℤ in Homotopy Type Theory. nya~
For context: in classical algebraic topology this is a foundational theorem, and the standard proof constructs the universal cover as the "winding map" ℝ → S¹, using the facts that ℝ is contractible and the fiber of the winding map is ℤ. But how do we do this in HoTT, where S¹ is a higher inductive type rather than a subset of ℝ²?
base : S¹ and a path constructor loop : base ≡ base. The fundamental group π₁(S¹) is the set-truncation of the loop space Ω¹(S¹) = (base ≡ base).
The whole approach hinges on the encode-decode method. Here's the setup. We define a type family code : S¹ → 𝒰 that serves as the universal cover. The magic is using Voevodsky's univalence axiom to define what happens when we go around the loop.
The key insight: succEquiv : ℤ ≃ ℤ is an equivalence (succ is both left and right invertible by pred), so by univalence, ua(succEquiv) : ℤ ≡ ℤ is an actual path in the universe. We use this as the path code(loop), which defines how the fiber transforms as we go around the loop. This is exactly the covering space intuition: the fiber over base is ℤ, and going around loop once corresponds to shifting up by 1.
Now we define the encode and decode maps:
The beautiful thing is that encode literally computes the winding number of a loop! Transport along a path p : base ≡ base in the code family shifts ℤ by the number of times the path winds around the circle. So encode(loop) = 1, encode(loop ∙ loop) = 2, encode(loop⁻¹) = -1, etc.
The proof that encode and decode are mutual inverses is the hard part. The "easy" direction is:
The hard direction — decode(x, encode(x, p)) = p for all x : S¹ and p : base ≡ x — requires path induction (J-rule). When p ≡ refl, we have encode(base, refl) = 0 and decode(base, 0) = refl, so it works out. The general case follows by J.
And since ℤ is a set (h-set, i.e. 0-truncated), we have ‖ℤ‖₀ = ℤ, so:
Has anyone formalized this in Cubical Agda recently? The Licata–Shulman (LICS 2013) version in HoTT-Agda is gorgeous but uses --without-K --rewriting axioms for HITs. I'm curious whether Cubical Agda makes the decode(loop i) clause cleaner since ua holds definitionally there. Anyone played with cubical Agda's S¹?