my blog lives here now
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

637 lines
39 KiB

7 years ago
3 years ago
7 years ago
3 years ago
7 years ago
3 years ago
7 years ago
3 years ago
7 years ago
3 years ago
7 years ago
3 years ago
7 years ago
3 years ago
7 years ago
  1. ---
  2. title: On Induction
  3. date: January 15th, 2021
  4. ---
  5. <!--
  6. battle plan:
  7. - motivate induction via lists
  8. - close enough to naturals that we can appeal to people's intuition about those
  9. - a general schema for normal induction (easy enough)
  10. - handwave about induction-induction (some syntax?) and induction-recursion (a universe?)
  11. - handwave quotient & higher induction-induction
  12. -->
  13. [Last time] on this... thing... I update _very occasionally_, I talked about possible choices for representing equality in type theory. Equality is very important, since many properties of programs and mathematical operators are stated as equalities (e.g., in the definition of a group). However, expressing properties is useless if we can't prove them, and this is where inductive types come in.
  14. [Last time]: /posts/reflections-on-equality.html
  15. Inductive types allow users to extend their type theories with new types, generated by _constructors_, which respect an _induction principle_. Constructors are conceptually simple: they provide a way of *construct*ing elements of that type, like the name implies. The induction principle, though, requires a bit more thought.
  16. The only inductive type
  17. -----------------------
  18. Since this is a blog post about type theory, I am required, by law, to start this discussion about inductive types with the most boring one: the set of all natural numbers, $\mathbb{N}$. The set $\mathbb{N}$ (to be read, and written, `Nat`, from now on) is both uninteresting enough that the discussion can focus on the induction aspect, but also non-trivial enough for some complications to arise.
  19. The type `Nat`{.dt} is generated by two constructors, one for `zero`{.dt}, and one for the `suc`{.dt}cessor of another number. We can represent this in Agda as in the code block below. Taken together, the signature for the type former (`Nat : Set`{.agda}) and the signatures of all the constructors make up the complete specification of the inductive type, called (confusingly) a signature. Looking at only these we can figure out its fundamental property—its induction principle.
  20. ```agda
  21. data Nat : Set where
  22. zero : Nat
  23. suc : Nat → Nat
  24. ```
  25. Before we consider the full induction principle, it's useful to consider a simply-typed restriction, called the _recursor_. Every inductive type has a recursor, but this name makes the most sense when talking about natural numbers, because it operationally models primitive recursion. The recursor for the natural numbers is a (dependent) function with the following parameters:
  26. - A _motive_, the type we elminate into;
  27. - A _method_ for the constructor `zero`, that is, an element of `A`, and
  28. - A _method_ for the constructor `suc`, which is a function from `A → A`.
  29. To put it into code:
  30. ```agda
  31. foldNat : (A : Type) → A → (A → A) → Nat → A
  32. ```
  33. Furthermore, the recursor has, as anything in type theory, computational behaviour. That is, when applied to all of its arguments, `foldNat` evaluates. This evaluation is recursive, but pay attention to the structure: we only ever apply `foldNat` to values "smaller" (contained in) the data we started with.
  34. ```agda
  35. foldNat : (A : Type) → A → (A → A) → Nat → A
  36. foldNat A z s zero = z
  37. foldNat A z s (suc x) = s (foldNat A z s x)
  38. ```
  39. To see how one derives the recursor from the signature, we would have to take a detour through category theory. While a complete semantic argument is preferable, for now, some handwaving is in order. To cite my sources: this presentation of algebras was given in [Kaposi 2020], a very well-written paper which you should check out if you want to understand how algebras work for higher inductive types.
  40. [Kaposi 2020]: https://arxiv.org/pdf/1902.00297.pdf
  41. Take a **`Nat`{.dt}-algebra** to be a dependent triple `(C : Set) × C × (C → C)`{.agda}. The type `C` is known as the _carrier_ of the algebra. From this type of algebras we can define a type family of _`Nat`{.dt}-algebra homomorphisms_ between two algebras, which are structure-preserving maps between them. This definition is a bit complicated, so it's in a box:
  42. ```agda
  43. Alg : Set₁
  44. Alg = Σ Set₀ λ Nat → Σ Nat λ _ → (Nat → Nat)
  45. Morphism : Alg → Alg → Set
  46. Morphism (N0 , z0 , s0) (N1 , z1 , s1) =
  47. Σ (N0 → N1) λ NM →
  48. Σ (NM z0 ≡ z1) λ _ →
  49. ((n : N0) → NM (s0 n) ≡ s1 (NM n))
  50. ```
  51. A morphism between algebras $(N_0, z_0, s_0)$ and $(N_1, z_1, s_1)$ consists of a map $N^M : N_0 \to N_1$ between their carriers together with data proving that $N^M$ maps the objects in $N_0$ to the "right" objects in $N_1$.
  52. Suppose for a second that we have a "special" `Nat`{.dt}-algebra, denoted `Nat*`{.dt}. The statement that there exists a recursion principle for the `Nat`{.dt}ural numbers is then a function with the type boxed below.
  53. ```agda
  54. recursion : (α : Alg) → Morphism Nat* α
  55. ```
  56. To borrow some more terminology from our category theorist friends, it's stating that `Nat*`{.dt} is _weakly initial_ in the category of `Nat`{.dt}-algebras: For every other algebra `α` we have a homomorphism `Nat* → α`. It turns out that, if our type theory has inductive types, we can implement this function! The initial algebra is given by `(Nat, zero, suc)` and the `recursion` principle by the function below:
  57. ```agda
  58. Nat* : Alg
  59. Nat* = (Nat, zero, suc)
  60. recursion : (α : Alg) → Morphism Nat* α
  61. recursion (N0 , z0 , s0) = map , refl , λ n → refl where
  62. map : Nat → N0
  63. map zero = z0
  64. map (suc x) = s0 (map x)
  65. ```
  66. In particular, to be very needlessly formal, "every inductive type is the _initial algebra_ of some strictly-positive endofunctor." It's no surprise, then, that we can give such an initial algebra for the natural numbers in Agda, a theory with inductive types! The "shape" of this functor depends on the constructors of the inductive type, but the general principle is the same: Data representing the type (the carrier) together with data for every constructor, with the inductive type "replaced" by the carrier.
  67. And then some more
  68. ------------------
  69. A trivial inductive type we can discuss is the unit type, or top type:
  70. ```agda
  71. data Unit : Set where
  72. point : Unit
  73. ```
  74. Let's speedrun through the definition of `Unit`{.dt}-algebras, `Unit`{.dt}-algebra homomorphisms, and the initial `Unit`{.dt}-algebra. The name `point`{.dt} was picked for the constructor to be indicative of these structures: `Unit`{.dt} is the _initial pointed type_. A pointed type `T` is a type for which we have a point `p : T`—oops, there's our algebra type! It's `(T : Set) × T`.
  75. _Wait, what? Really?_, I hear you ask. You proceed: _An algebra for the `Unit` type is just a pointed type?_ Yup, my dear reader: the only data we need to eliminate from `Unit` is the type we're eliminating into (the carrier `C`) and a value in `C`, that we map our `point` to!
  76. The family of types between unit types can also be specified in Agda, but I'm absolutely sure you can work it out: It's a function $U^M : U_0 \to U_1$ such that $U^M p_0 \equiv p_1$.
  77. ```agda
  78. Unit-Alg : Set₁
  79. Unit-Alg = Σ Set λ Unit → Unit
  80. Unit-Morphism : Unit-Alg → Unit-Alg → Set
  81. Unit-Morphism (U0 , p0) (U1 , p1) = Σ (U0 → U1) λ UM → UM p0 ≡ p1
  82. ```
  83. Our definition for Unit-recursion is laughable. It's so trivial that I'm going to go a bit further, and instead of proving _weak_ initiality as we did for the naturals, prove _initiality_: There exists a **unique** map from `(Unit, point)` to any other algebra. For this, we need a notion of uniqueness. Contractibility will do: A type is contractible iff it's pointed and, for every other element, there exists an equality between the point and that element. Check out the box:
  84. ```agda
  85. isContr : Set → Set
  86. isContr T = Σ T λ center → (other : T) → other ≡ center
  87. ```
  88. Or, this is where I _would_ put a nice box together with an Agda-checked proof that `(Unit , point)` is initial... if I had one! Agda does not have a strong enough notion of equality for us to prove this. In particular, Agda out-of-the-box has no _function extensionality_ principle (see my post on equality). However, we _can_ postulate one, and after writing some equality-manipulation functions, write that proof.
  89. <details>
  90. <summary>Equality stuff</summary>
  91. ```agda
  92. postulate
  93. funext : {ℓ ℓ' : _}
  94. {A : Set ℓ} {B : A → Set ℓ'}
  95. (f g : (x : A) → B x)
  96. → ((x : A) → f x ≡ g x) → f ≡ g
  97. subst : {ℓ ℓ' : _}
  98. {A : Set ℓ} (B : A → Set ℓ')
  99. {x y : A}
  100. → x ≡ y → B x → B y
  101. subst B refl x = x
  102. pairPath : {ℓ ℓ' : _}
  103. {A : Set ℓ}
  104. {B : A → Set ℓ'}
  105. {a b : Σ A B}
  106. (p : (a ₁) ≡ (b ₁))
  107. → subst B p (a ₂) ≡ (b ₂)
  108. → a ≡ b
  109. pairPath {_} {_} {A} {B} {(a , b)} {(a' , b')} refl refl = refl
  110. _∘_ : {ℓ : _} {A : Set ℓ} {x y z : A} → y ≡ z → x ≡ y → x ≡ z
  111. refl ∘ refl = refl
  112. UIP : {ℓ : _} {A : Set ℓ} {x y : A} (p q : x ≡ y) → p ≡ q
  113. UIP refl refl = refl
  114. ```
  115. </details>
  116. I'll be the first to admit that all this equality stuff, _especially_ the postulate, is a bit gross. However, it's all justifiable: In particular, the setoid model of type theory validates all of MLTT + funext + UIP, so we're in the clear. The `pairPath` function has a very complicated type: it defines the setoid (or groupoid!) structure of the $\sum$-types.
  117. If you can intuit that an equality between `(a, b) ≡ (c, d)` consists of a pair of equalities `(a ≡ c) × (b ≡ d)`, `pairPath` is that, but dependent. Since the second components of the pairs have types depending on the first element, we need to coerce `e` along the path between `x ≡ y` to get an element `subst B p e : B y` that we can compare with `e'`.
  118. ```agda
  119. Unit-initiality : (α : Unit-Alg)
  120. → isContr (Unit-Morphism (Unit , point) α)
  121. Unit-initiality (U0 , p0) = (map , refl) , contract where
  122. map : Unit → U0
  123. map point = p0
  124. contract : (other : Unit-Morphism (Unit , point) (U0 , p0))
  125. → other ≡ (map , refl)
  126. contract ( fun , p ) =
  127. pairPath
  128. (funext fun map (λ { point → p }))
  129. (UIP _ _)
  130. ```
  131. With funext, we can also prove `Nat-initiality`, the equivalent statement about natural numbers. Left as an exercise to the reader, though!
  132. Upgrading your recursion: displayed algebras
  133. --------------------------------------------
  134. Just like we can go from regular pairs to dependent pairs by complicating things, we can complicate the recursor to get an _induction principle_, which uniquely characterises each inductive type. From an algebra (the input to the recursor), we build a **displayed algebra**, which is a logical predicate over the algebra's carrier together with proofs for each of its methods. Let's go back to the natural numbers for a second, since those are more interesting than the unit type.
  135. Given a $\mathbb{N}$-algebra $\alpha = (N, z, s)$, we define the type of displayed $\mathbb{N}$-algebras over $\alpha$ to be $\sum(N^D : N \to \mathrm{Set}) \sum(z^D : N^D z) (\prod(n : N) N^D n \to N^D (s\ n))$. That is, we "upgrade" the carrier $N$ to a predicate on carriers $N \to \mathrm{Set}$, $z$ gets upgraded to a proof of $N^D z$, and $s$ gets upgraded to a proof that $N^D (s\ n)$ follows from $N^D n$. Check out the box:
  136. ```agda
  137. Nat-Displayed : (α : Nat-Alg) → Set₁
  138. Nat-Displayed (N , z , s) =
  139. Σ (N → Set) λ Nd →
  140. Σ (Nd z) λ _ ->
  141. (n : N) → Nd n → Nd (s n)
  142. ```
  143. Now we can finally connect the idea of "inductive types" back to the idea of "induction" that some of us got familiar with in a high school proofs class, namely over the natural numbers:
  144. > Suppose we have a property of the natural numbers, $P$. If:
  145. >
  146. > - $P$ holds for $0$, and
  147. > - Assuming P holds for n, P holds for $n + 1$, then
  148. >
  149. > $P$ holds for every natural number.
  150. This is the same thing we have encoded in the displayed algebras over $(N, z, s)$! Since a predicate is, type-theoretically, a family of types, we interpret our $P$ as $N^D : N \to \mathrm{Set}$. A predicate holds for a value iff you have an inhabitant of the predicate applied to that value, so our proofs become terms of the right type. The universal quantifier for $n$ becomes a dependent function, and the implication a regular function.
  151. The final piece of our complicated terminology puzzle is the idea of a **section** of a displayed algebra over an algebra, a sentence so utterly convoluted that I wish I had never written it. Just like displayed algebras complicate the idea of algebras, sections complicate algebra homomorphisms. Just like a homomorphism has a map between the algebras' carriers, a section has a dependent map from algebra's carrier to the predicate of the algebra displayed over that one. The dependency structure doesn't make a lot of sense written out, so check out the code:
  152. ```agda
  153. Nat-Section : (α : Nat-Alg) → Nat-Displayed α → Set
  154. Nat-Section (N , z , s ) (Nd , zd , sd ) =
  155. Σ ((n : N) → Nd n) λ Ns →
  156. Σ (Ns z ≡ zd) λ _ →
  157. ((n : N) → Ns (s n) ≡ sd n (Ns n))
  158. ```
  159. The first component of a section is the induced function generated by the motives packaged together by the displayed algebra $(N^D, z^D, s^D)$. The second two components are _reduction rules_, packaged as propositional equalities! The second component, for instance, specifies that $N^S$ (the function) applied to the $z$ero of our algebra returns the method $z^D$.
  160. The existence of an induction principle is packaged up by saying that any algebra displayed over the initial one has a section. In code, it's a dependent map `Induction : (M : Nat-Displayed Nat*) → Section M`{.agda}. We can implement that in Agda like in the box below, but because the `map` obeys the computation rules automatically, none of the proof components are incredibly interesting—they're both trivial proofs.
  161. ```agda
  162. Nat-induction : (M : Nat-Displayed Nat*) → Nat-Section Nat* M
  163. Nat-induction (Nd , zd , sd) = map , refl , λ s → refl where
  164. map : (n : Nat) → Nd n
  165. map zero = zd
  166. map (suc x) = sd x (map x)
  167. ```
  168. As an _incredibly_ convoluted example we can use all our algebra machinery to implement.. drumroll, please... addition!
  169. ```agda
  170. add : Nat → Nat → Nat
  171. add = (Nat-induction displayed) ₁ where
  172. displayed : Nat-Displayed Nat*
  173. displayed = (λ n → Nat → Nat)
  174. , (λ z → z)
  175. , λ _ k z → suc (k z)
  176. _ : add (suc (suc zero)) (suc (suc zero))
  177. ≡ suc (suc (suc (suc zero)))
  178. _ = refl
  179. ```
  180. Again using our trivial example as a bit more practice before we move on, let's talk displayed algebras and sections for the Unit type. They're both quite easy. Assuming $(U, p)$ is our algebra, the displayed algebra ends up with a predicate $U^D : U → \mathrm{Set}$ together with a proof $p^D : U\ p$. Sections are also quite easy, since there's only one "propositional reduction rule" to give.
  181. ```agda
  182. Unit-Displayed : Unit-Alg → Set₁
  183. Unit-Displayed (U , p) = Σ (U → Set) λ UD → UD p
  184. Unit-Section : (α : Unit-Alg) → Unit-Displayed α → Set
  185. Unit-Section (U , p) (UD , pD) =
  186. Σ ((x : U) → UD x) λ US →
  187. US p ≡ pD
  188. ```
  189. Agda once more makes the proofs for writing `Unit-Induction` trivial, so I'll leave both its type and implementation as an exercise for the reader.
  190. You saw it coming: Vectors
  191. --------------------------
  192. Both of the exceedingly interesting types we looked at above were simple, since they both just exist in a universe, `Set₀`. Now we up the difficulty considerably by adding both _parameters_ and _indices_. Both are arguments to the type itself, but they differ in how they can be used in the constructors. Again, since I am writing about dependent types, I'm obligated by law to mention this example.. fixed-length lists, or vectors. These are _parametrised_ by a type $A$ of elements and _indexed_ by a number $n : \mathbb{N}$, the length.
  193. ```agda
  194. data Vec (A : Set₀) : Nat → Set₀ where
  195. nil : Vec A zero
  196. cons : (n : Nat) → A → Vec A n → Vec A (suc n)
  197. ```
  198. Parameters, at least in Agda, are introduced before the `:` in the type signature. Indices come later, but both can be dependent. Parameters have to be the same for every constructor (we only ever mention `Vec A`), but the `Nat`ural can vary between the constructors. One introduces a vector of length zero, and one increments the length of a smaller vector.
  199. The type of algebras for vectors is a tad more interesting, so let's take a look. Since `Vec` is parametrised by `A`, so is `Vec-Alg`. We don't speak of general "vector algebras", only "vector-of-A algebras", so to speak. Since the natural number is an index, it becomes an argument to our carrier type. Both of the "constructor" data are applied to their indices, and since the `A` is bound as an argument to the algebra function, we can refer to it in the type of `cons` (the third component).
  200. ```agda
  201. Vec-Alg : Set₀ → Set₁
  202. Vec-Alg A =
  203. Σ (Nat → Set₀) λ Vec →
  204. Σ (Vec zero) λ nil →
  205. {- cons : -} (n : Nat) → A → Vec n → Vec (suc n)
  206. ```
  207. The type of homomorphisms for `Vec A`-algebras is mechanically derived from the type above by starting from a map `(n : Nat) → V0 n → V1 n` (where `V0`, `V1` are the carriers of the "source" and "target" algebras) and imposing the necessary conditions.
  208. ```agda
  209. Vec-Morphism : (A : Set₀) → Vec-Alg A → Vec-Alg A → Set₀
  210. Vec-Morphism A (V0 , n0 , c0) (V1 , n1 , c1) =
  211. Σ ((n : Nat) → V0 n → V1 n) λ VM →
  212. Σ (VM zero n0 ≡ n1) λ _ →
  213. ((n : Nat) (a : A) (xs : V0 n)
  214. → VM (suc n) (c0 n a xs) ≡ c1 n a (VM n xs))
  215. ```
  216. Just like in the Nat-algebra morphisms, we have two cases, one of which requires exchanging our map with a constructor—the successor/cons case. It's mostly the same, except for the `A` parameter we have to thread everywhere, and all the indices we have to respect. And, just like back then, assuming funext, we have an initial `Vec A`-algebra given by `(Vec A, nil, cons)`. The proof is full of fiddly details[^1], but it's again justified by the setoid model, with only `funext` needing to be postulated.
  217. ```agda
  218. Vec-initiality : (A : Set) (α : Vec-Alg A)
  219. → isContr (Vec-Morphism A (Vec A , nil , cons) α)
  220. Vec-initiality A (V0 , n0 , c0) = (map , resp) , contract where
  221. map : (n : Nat) → Vec A n → V0 n
  222. map zero nil = n0
  223. map (suc n) (cons .n a xs) = c0 n a (map n xs)
  224. resp = refl , λ n a x → refl
  225. ```
  226. Above we provide the map from `Vec A n` to the algebra `α`'s carrier, `V0`, and prove that it `resp`{.kw}ects the equalities imposed by `Vec A`-homomorphisms. Since Agda has pattern matching built in, these equalities are trivial (`refl`{.agda}). Our proof continues upside-down: The interesting thing here is the `map'≡map` equality. Skip the noise: keep reading after the code.
  227. ```{.agda .continues}
  228. contract : (other : Vec-Morphism A (Vec A , nil , cons)
  229. (V0 , n0 , c0))
  230. → other ≡ (map , refl , λ n a xs → refl)
  231. contract (map' , nM , cM)
  232. = pairPath
  233. map'≡map
  234. (pairPath
  235. (UIP _ _)
  236. (funext3 _ _ (λ n a xs → UIP _ _)))
  237. where
  238. map'~map : (n : Nat) (xs : Vec A n) → map' n xs ≡ map n xs
  239. map'~map .zero nil = nM
  240. map'~map .(suc n) (cons n x xs) =
  241. subst
  242. (λ e → map' (suc n) (cons n x xs) ≡ c0 n x e)
  243. (map'~map n xs)
  244. (cM n x xs)
  245. map'≡map = funext2 _ _ map'~map
  246. ```
  247. We build the `map'≡map`{.va} equality by an appeal to `funext2`{.va}, a helper defined in terms of our `funext`{.op} postulate to reduce the noise a tad. Again, I want to stress that this is a failure of Agda: There is a family of semantic models of type theory in which equality in function types is pointwise _by definition_, including the setoid model, which would be very convenient here since we could keep our appeals to `UIP`{.op}. Anything that validates `funext`{.op} would do, though, including Cubical Agda.
  248. Other than that, the proof is standard: Everything we need is given by the `nM`{.va} and `cM`{.va} equalities of the `other`{.va} morphism we're contracting. In the inductive step, the case for `(cons n x xs)`, we have a path `cM n x xs : map' (suc n) (cons n x xs) ≡ c0 n x (map' n xs)`. Since we want the `map' n xs` on the right-hand side to be `map n xs`, we `subst`{.op} it away using the path `map'~map n xs : map' n xs ≡ map n xs` obtained by a recursive application of `map'~map`{.fn}.
  249. Now we turn to dependent elimination of vectors, or induction. For this we need to calculate the type of displayed algebras over a given `Vec A`-algebra. It's mechanical: The `V` of our algebra gets upgraded to a family of predicates `P : (n : Nat) → V n → Set₀`. The "constructor data" given by `n , c` become "proof data" of a type extending the `Vec A`-algebra.
  250. ```agda
  251. Vec-Displayed : (A : Set₀) → Vec-Alg A → Set₁
  252. Vec-Displayed A (V , z , c) =
  253. Σ ((n : Nat) → V n → Set₀) λ P →
  254. Σ (P zero z) λ nil →
  255. {- cons -} (n : Nat) (x : A) (tail : V n)
  256. → P n tail → P (suc n) (c n x tail)
  257. ```
  258. In the `cons` case, the algebra has access both to the `tail` of type `V n` _and_ the inductive assumption `P n tail`. This represents, operationally, a choice between recurring or not.
  259. You know the drill by now: after displayed algebras, algebra sections. Assume an algebra `α = (V, nil, cons)` and a displayed algebra `(P, nD, cD)` over `α`. The first component will be a dependent function of type `(n : Nat) (x : V n) → P n x`, and the second and third components will be propositional equalities representing reduction rules. Since the vectors and natural numbers are similar in structure, we'll end up with similar-looking reduction rules, just noisier. Here they are:
  260. ```agda
  261. Vec-Section : (A : Set₀) (α : Vec-Alg A) → Vec-Displayed A α → Set
  262. Vec-Section A (V , n , c) (P , nD , cD) =
  263. Σ ((n : Nat) (x : V n) → P n x) λ map →
  264. Σ (map zero n ≡ nD) λ _ →
  265. ( (n : Nat) (x : A) (tl : V n)
  266. → map (suc n) (c n x tl) ≡ cD n x tl (map n tl))
  267. ```
  268. They're not pretty, so take a minute to internalise them. Again, we have the dependent `map`{.op}, together with a propositional equality which says `map zero n` evaluates to the `nD` datum of our displayed map. The third component specifies that `map (suc n)` defers to the `cD` component and recurs in the tail. It's not complicated conceptually, but it is complex to write down. Finally, the induction principle for natural numbers says that any displayed algebra over the initial `(Vec A , nil , cons)` has a section. Again, Agda trivialises the equalities:
  269. ```agda
  270. Vec-Induction : (A : Set₀)
  271. (α : Vec-Displayed A (Vec A , nil , cons))
  272. → Vec-Section A (Vec A , nil , cons) α
  273. Vec-Induction A (P , nD , cD) = map , refl , λ n a xs → refl where
  274. map : (n : Nat) (xs : Vec A n) → P n xs
  275. map .zero nil = nD
  276. map .(suc n) (cons n x xs) = cD n x xs (map n xs)
  277. ```
  278. I bet you've never seen types like these
  279. ----------------------------------------
  280. This is a bet I'm willing to take. Unless you've gone looking for ways to formalise semantics of inductive types before, it's not very likely for you to have come across this algebra machinery before. A far more common way of dealing with inductive data types is dependent pattern matching:
  281. ```agda
  282. add : Nat → Nat → Nat
  283. add zero n = n
  284. add (suc x) n = suc (x + n)
  285. map : {n : Nat} {A B : Set₀} → (A → B) → Vec A n → Vec B n
  286. map f nil = nil
  287. map f (cons x xs) = cons (f x) (map f xs)
  288. ```
  289. However, there is a very good reason not to work directly with pattern matching in a formalisation. That reason can be found on page 24 of [this paper] by Cockx et al. Take a minute to recover and we can continue talking. A more tractable presentation of inductive types is directly with induction principles, instead of taking this detour through category theory. For instance, the natural numbers:
  290. ```agda
  291. Nat-induction : (P : Nat → Set)
  292. → P zero
  293. → ((n : Nat) → P n → P (suc n))
  294. → (n : Nat) → P n
  295. Nat-induction P pz ps zero = pz
  296. Nat-induction P pz ps (suc n) = ps n (Nat-induction P pz ps)
  297. ```
  298. It turns out that this presentation, and the one based on algebras I've been going on about, are actually equivalent! Very plainly so. If you unfold the type of `Nat-Induction` (the one about algebras), and do some gratuitous renaming, you get this:
  299. ```agda
  300. Nat-Induction :
  301. (M : Σ (Nat → Set)
  302. λ P → Σ (P zero)
  303. λ _ → (n : Nat) → P n → P (suc n)
  304. )
  305. → Σ ((n : Nat) → M ₁ n)
  306. λ Nat-ind → Σ (Nat-ind zero ≡ M ₂ ₁)
  307. λ _ → (n : Nat) → Nat-ind (suc n) = M ₂ ₂ n (Nat-ind n)
  308. ```
  309. It's ugly, especially with all the subscript projections, but if we apply some good ol' currying we can recover this type, by transforming (the dependent analogue of) `A × B × C → D` with `A → B → C → D`:
  310. ```agda
  311. Nat-Induction : (P : Nat → Set)
  312. (pzero : P zero)
  313. (psuc : (n : Nat) → P n → P (suc n))
  314. → Σ ((n : Nat) → P n)
  315. λ ind → Σ (ind zero ≡ pzero)
  316. λ _ → (n : Nat) → ind (suc n) ≡ psuc n (ind n)
  317. ```
  318. I've gone and done some more renaming. `P` is a name for `M ₁`, `pzero` is `M ₂ ₁`, and `psuc` is `M ₂ ₂`. Again, just to emphasise what the contents of "a displayed algebra section over the initial algebra" turn out to be:
  319. - The first component is **a function from the data type to a predicate**,
  320. - The $n$ following components are **propositional equalities representing reduction rules**, for each constructor, recurring as appropriate.
  321. If you'll allow me one of _those_ asides, this is just incredibly cool to me. The fact we can start from an inductive signature and derive not only the recursor, but from that derive the motives and methods of induction, and from _that_ derive exactly the behaviour of the induced map for a particular bundle of motives and methods? It feels.. right. Like this is what induction was meant to be. Not some needlessly formal underpinning for pattern matching, like I used to think of it as, but as distinguished, rich mathematical objects determined exactly by their constructors.
  322. Complicating it further: Induction-induction
  323. --------------------------------------------
  324. We've seen, in the past 2 thousand-something words, how to build algebras, algebra homomorphisms, displayed algebras and algebra sections for inductive types, both indexed and not. Now, we'll complicate it further: Induction-induction allows the formation of two inductive families of types $A : \mathrm{Set}$ and $B : A \to \mathrm{Set}$ _together_, such that the constructors of A can refer to those of B and vice-versa.
  325. The classic example is defining a type together with an inductively-defined predicate on it, for instance defining sorted lists together with a "less than all elements" predicate in one big induction. However, in the interest of brevity, I'll consider a simpler example: A syntax for contexts and Π-types in type theory. We have one base type, `ι`, which is valid in any context, and a type for dependent products `Π`. Its type expresses the rule for $\prod$-introduction, which says that given $\Gamma \vdash \sigma\ \mathrm{type}$ and $\Gamma, \sigma \vdash \tau\ \mathrm{type}$, then $\Gamma \vdash (\prod(\sigma) \tau)\ \mathrm{type}$.
  326. ```agda
  327. data Ctx : Set
  328. data Ty : Ctx → Set
  329. data Ctx where
  330. stop : Ctx
  331. pop : (Γ : Ctx) → Ty Γ → Ctx
  332. data Ty where
  333. ι : {Γ : Ctx} → Ty Γ
  334. Π : {Γ : Ctx} (σ : Ty Γ) (τ : Ty (pop Γ σ)) → Ty Γ
  335. ```
  336. Here I'm using Agda's forward-declaration feature to specify the signatures of `Ctx` and `Ty` before their constructors. This is because the constructors of `Ctx` mention the type `Ty`, and the type _of_ `Ty` mentions `Ctx`, so there's no good way to untangle them.
  337. When talking about our standard set of categorical gizmos for inspecting values of inductive type, it's important to note that, just like we can't untangle the definitions of `Ctx` and `Ty`, we can't untangle their algebras either. Instead of speaking of `Ctx`-algebras or `Ty`-algebras, we can only talk of `Ctx-Ty`-algebras.[^2] Let's think through the algebras first, since the rest are derived from that.
  338. For our `Nat`-algebras, we had as a carrier an element of `Set₀`. For the `Vec`-algebras, we needed a carrier which was an element of `Nat → Set₀`. Now, since we have two types, one of which is indexed, we can't describe their carriers in isolation: We have a _telescope_ of carriers $Σ (\mathrm{Ty}^A : \mathrm{Set}_0) Σ (\mathrm{Ctx}^A : \mathrm{Ty}^A \to \mathrm{Set}_0)$ over which the type of the methods is quantified. Let's go through them in order.
  339. - `stop : Ctx` gives rise to a method $\mathrm{stop}^A : \mathrm{Ctx}^A$,
  340. - `pop : (Γ : Ctx) → Ty Γ → Ctx` generates a method $\mathrm{pop}^A : (Γ^A : \mathrm{Ctx}^A) → \mathrm{Ty}^A\ Γ^A → \mathrm{Ctx}^A$
  341. - `ι : {Γ : Ctx} → Ty Γ` leads us to jot down a method $\iota^A : \{Γ^A : \mathrm{Ctx}^A\} → \mathrm{Ty}^A\ \Gamma^A$
  342. - `Π : {Γ : Ctx} (σ : Ty Γ) (τ : Ty (pop Γ σ)) → Ty Γ` finally gives us a method with the hell-of-a-type
  343. $\Pi^A : \{\Gamma^A : \mathrm{Ctx}^A\} (\sigma^A : \mathrm{Ty}^A\ \Gamma) (\tau : \mathrm{Ty}^A (\mathrm{pop}^A\ \Gamma^A\ \sigma^A) → \mathrm{Ty}^A\ \Gamma^A)$
  344. Let's go back to Agda-land, where I will write exactly the same thing but with syntax somehow even less convenient than LaTeX. Just as a reminder, `Σ A λ x → B` is how I've been writing $\sum(x : A) B$.
  345. ```agda
  346. Ctx-Ty-Alg : Set₁
  347. Ctx-Ty-Alg =
  348. Σ Set₀ λ Ctx →
  349. Σ (Ctx → Set₀) λ Ty →
  350. Σ Ctx λ stop →
  351. Σ ((Γ : Ctx) → Ty Γ → Ctx) λ pop →
  352. Σ ({Γ : Ctx} → Ty Γ) λ ι →
  353. ({Γ : Ctx} (σ : Ty Γ) (τ : Ty (pop Γ σ)) → Ty Γ)
  354. ```
  355. The type of homomorphisms for these algebras are the same as the ones we've seen before, except for the fact they're way more complicated. Both of the carrier components still become functions, and all of the method components still become equalities which must be respected, except now there's more of them.
  356. ```{.agda tag=Suffering}
  357. Ctx-Ty-Morphism : Ctx-Ty-Alg → Ctx-Ty-Alg → Set₀
  358. Ctx-Ty-Morphism
  359. (C0 , T0 , s0 , p0 , i0 , f0)
  360. (C1 , T1 , s1 , p1 , i1 , f1)
  361. =
  362. Σ (C0 → C1) λ Ctx →
  363. Σ ((x : C0) → T0 x → T1 (Ctx x)) λ Ty →
  364. -- Constructor data for Ctx
  365. Σ (Ctx s0 ≡ s1) λ stop →
  366. Σ ( (x : C0) (ty : T0 x)
  367. → Ctx (p0 x ty) ≡ p1 (Ctx x) (Ty x ty))
  368. λ pop →
  369. -- Constructor data for Ty
  370. Σ ({Γ : C0} → Ty Γ (i0 {Γ}) ≡ i1 {Ctx Γ}) λ iota →
  371. ( {Γ : C0} (σ : T0 Γ) (τ : T0 (p0 Γ σ))
  372. → Ty Γ (f0 {Γ} σ τ)
  373. ≡ f1 (Ty Γ σ) (subst T1 (pop Γ σ) (Ty (p0 Γ σ) τ)))
  374. ```
  375. Ok, I lied. There's more than just the usual "more stuff" complication. In the case for `Π` (nameless, the last component), we need to compare `f0 σ τ` with `f1 (Ty Γ σ) (Ty (p0 Γ σ) τ)`. Read past the symbols: We need to make sure that the `Ty` operation maps `Π` types to `Π` types. But, there's a catch! `Ty (p0 Γ σ) τ)` has type `T1 (Ctx (p0 Γ σ))` (i.e., assemble the context in `C0` with `p0`, then translate to `C1` with `T1`), but `f1` wants something in `T1 (p1 (Ctx Γ) (Ty Γ σ))` (assemble the context in `C1` with translated components).
  376. We know these types are equal though, the equation `pop` says _just that_! Agda isn't that clever, though (thankfully), so we have to manually cast along the path `pop Γ σ` to make these types line up. Since we have so many equations, we definitely would've ended up with at least one dependent path.[^3]
  377. I'll pass on proving strict initiality in the interest of brevity, settling for the weak variant. Recall that initiality means we have a distinguished `Ctx-Ty`-algebra, given by the type formers and constructors, from which we can make a morphism to any other algebra. In types:
  378. ```agda
  379. Ctx-Ty-Initial : (M : Ctx-Ty-Alg) → Ctx-Ty-Morphism Ctx-Ty* M
  380. Ctx-Ty-Initial (C0 , T0 , s0 , p0 , i0 , f0)
  381. = C , T , refl , (λ x ty → refl) , refl , λ σ τ → refl
  382. where
  383. C : Ctx → C0
  384. T : (x : Ctx) → Ty x → T0 (C x)
  385. C stop = s0
  386. C (pop c t) = p0 (C c) (T c t)
  387. T γ ι = i0 {C γ}
  388. T γ (Π σ τ) = f0 (T γ σ) (T (pop γ σ) τ)
  389. ```
  390. I want to make clear that, while this looks like a bloody mess, it was incredibly simple to implement. See the equation `T γ (Π σ τ) = ...`? I didn't write that. I didn't _need_ to. Check out the type of the last component of `Ctx-Ty-Morphism Ctx-Ty* M`. It's literally just saying what the right-hand-side of `T γ (Π σ τ)` needs to be for `(C, T)` to be the morphism part of a **homo**morphism. And the homo (ahem) part is trivial! It's all `refl`s again (are you noticing a trend?).
  391. Elevating it: Higher Inductive Types
  392. ------------------------------------
  393. The algebra approach isn't limited to the `Set`-level mathematics we've been doing up to now, and it scales very simply to types with path constructors in addition to point constructors. For this part, we unfortunately can't _implement_ any of the proofs in standard Agda, especially Agda `--with-K` that we've been using up to now, but we can still talk about their types. Let's start with the second simplest higher inductive type: Not the circle, but the interval.
  394. The interval has two endpoints, `l` and `r`, and a line `seg`{.op}ment between them. It's a contractible type, so it's equivalent to the Unit type we've seen before, but it's.. more. The existence of the interval type implies funext, and we'll show that using algebras.
  395. ```agda
  396. data I : Type₀ where
  397. l r : I
  398. seg : i0 ≡ i1
  399. ```
  400. The type of algebras for the interval is also simple. We have a carrier, $I^A$, two points $l^A : I^A$ and $r^A : I^A$, and an _equality_ $seg^A : l^A \equiv r^A$. Wait, an equality? Yup! Since the enpoinds `l` and `r` are equal, so must be whatever they map to. But, since we're in HoTT-land now, this equality doesn't necessarily need to be trivial, as we'll see in a second.
  401. ```agda
  402. I-Alg : Type₁
  403. I-Alg = Σ Type₀ λ I → Σ I λ l → Σ I λ r → l ≡ r
  404. ```
  405. The type of algebra homomorphisms, like before, encompasses a map together with some propositional reductions of that map. We have three "standard" components `I`, `l`, `r`, which are just like for the type of booleans, and a mystery fourth component which expresses the right coherence condition.
  406. ```agda
  407. I-Alg-Morphism : I-Alg → I-Alg → Type
  408. I-Alg-Morphism (I0 , l0 , r0 , seg0) (I1 , l1 , r1 , seg1) =
  409. Σ (I0 → I1) λ I →
  410. Σ (I l0 ≡ l1) λ l →
  411. Σ (I r0 ≡ r1) λ r →
  412. ```
  413. Now the type of the fourth component is interesting. We want `seg0` and `seg1` to be "equal" in some appropriate sense of equality. But they have totally different types! `seg0 : l0 ≡I0 r0` and `seg1 : l1 ≡I1 r1`. It's easy enough to fix the type of `seg0` to be a path in `I1`: Since `I : I0 → I1`, its action on paths `ap I seg0` is a path in `I1` between `I l0` and `I r0`. Out of the pan, into the fire, though. Now the endpoints don't match up!
  414. Can we fix `I l0` and `I r0` in the endpoints of `seg0`? The answer, it turns out, is yes. We can use path _transitivity_ to alter both endpoints. `r` correctly lets us go from `I r0` to `r1`, but `l` is wrong—it takes `l1` to `I r0`. We want that backwards, so we apply _symmetry_ here.
  415. ```agda
  416. (r ∘ (ap I seg0 ∘ sym l)) ≡ seg1
  417. ```
  418. Now, I'll reproduce an argument from the HoTT book, which proves function extensionality follows from the interval type and its eliminator. But we'll do it with algebras. First, we need to postulate (since Agda is weak), that there exists a weakly initial `I`-algebra. This'll play the part of our data type. In reality, there's a couple more details, but we can ignore those, right?
  419. <details>
  420. <summary>We can't.</summary>
  421. Instead of just full-on postulating the existence of an `I`-algebra and its initiality, I'll define the type `I` (lacking its `seg`) normally, postulate `seg`, then postulate the coherence condition in the definition of `I-Recursion`. This simplifies some proofs since it means `I-Recursion` will compute definitionally on point constructors.
  422. ```agda
  423. data I : Type where
  424. l r : I
  425. postulate
  426. seg : l ≡ r
  427. I* : I-Alg
  428. I* = (I , l , r , seg)
  429. I-Recursion : (α : I-Alg) → I-Alg-Morphism I* α
  430. I-Recursion (I0 , l0 , r0 , seg0) = go , refl , refl , subst (λ e → e ≡ seg0) (∘refl (ap go seg)) sorry where
  431. go : I → I0
  432. go l = l0
  433. go r = r0
  434. postulate
  435. sorry : ap go seg ≡ seg0
  436. ```
  437. There's a bit of ugly equality-wrangling still, but it's fine. All that matters is that it type checks, I suppose.
  438. </details>
  439. Now, the argument. We want a function that, given $f, g : A \to B$ and a homotopy $p : \prod(x : A) \to f(x) \equiv g(x)$, produces a path $f \equiv g$. We define a family of functions $h_x : I \to B$ given a $x : A$, which maps `l` to `f(x)`, `r` to `g(x)`, and in the "`seg` case", returns `p(x)`. The algebra is simple:
  440. ```agda
  441. I→funext : {A B : Type} (f g : A → B) (hom : (x : A) → f x ≡ g x) → f ≡ g
  442. I→funext {A} {B} f g p = ? where
  443. h : A → I → B
  444. h x = (I-Recursion (B , f x , g x , p x)) ₁
  445. ```
  446. And for my next trick, I'll define the function `h' = flip h`. Or, in Agda terms, `h' = λ i x → h x i`, a function from `I → A → B`. We have a path between the two endpoints of the segment, so we also have a path between the two "endpoinds" of this function, which we can calculate:
  447. ```{.agda tag="Mmm, substitution"}
  448. ap h' seg : (λ i x → h x i) l ≡ (λ i x → h x i) r -- type of ap
  449. = (λ x → h x l) ≡ (λ x → h x r) -- β-reduce
  450. = (λ x → f x) ≡ (λ x → g x) -- computation for h
  451. = f ≡ g -- η-reduce
  452. ```
  453. This concludes our proof:
  454. ```agda
  455. I→funext : {A B : Type} (f g : A → B) (hom : (x : A) → f x ≡ g x) → f ≡ g
  456. I→funext {A} {B} f g p = ap h' seg where
  457. h : A → I → B
  458. h x = (I-Recursion (B , f x , g x , p x)) ₁
  459. h' : I → A → B
  460. h' i x = h x i
  461. ```
  462. This concludes our post
  463. -----------------------
  464. God, this was a long one. Four thousand words! And a bit more, with this conclusion.
  465. I've wanted to write about inductive types for a hecking long time now, ever since the day I finished the equality post. However, coming by motivations in the past year has been.. hard, for everyone. So it's good that I finally managed to it out there! _77 days later_. God. I might write some more type theory in the future, but don't hold your breath! 77 days is like, 36 thousand times more than you could do that for.
  466. I ended up writing about algebras instead of eliminators or pattern matching because they seamlessly scale up even to higher induction-induction, which ends up having super complicated eliminators, far too much for me to derive by hand (especially early in the AMs, which is when most of this post was written).
  467. <div class="special-thanks">
  468. With special thanks to the proofreader:
  469. * [My friend Jonathan](https://squiddev.cc)
  470. </div>
  471. [this paper]: https://dl.acm.org/doi/10.1145/3236770
  472. [^1]: And, full disclosure, took me almost an hour to write at 2 AM...
  473. [^2]: The best part of this presentation is that it makes painfully clear the infernal way that everything in type theory depends on everything else.
  474. [^3]: Please forgive my HoTT accent.