less prototype, less bad code implementation of CCHM type theory
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.

664 lines
23 KiB

  1. -- We begin by adding some primitive bindings using the PRIMITIVE pragma.
  2. --
  3. -- It goes like this: PRIMITIVE primName varName.
  4. --
  5. -- If the varName is dropped, then it's taken to be the same as primName.
  6. --
  7. -- If there is a previous declaration for the varName, then the type
  8. -- is checked against the internally-known "proper" type for the primitive.
  9. -- Universe of fibrant types
  10. {-# PRIMITIVE Type #-}
  11. -- Universe of non-fibrant types
  12. {-# PRIMITIVE Pretype #-}
  13. -- Fibrant is a fancy word for "has a composition structure". Most types
  14. -- we inherit from MLTT are fibrant:
  15. --
  16. -- Stuff like products Π, sums Σ, naturals, booleans, lists, etc., all
  17. -- have composition structures.
  18. --
  19. -- The non-fibrant types are part of the structure of cubical
  20. -- categories: The interval, partial elements, cubical subtypes, ...
  21. -- The interval
  22. ---------------
  23. -- The interval has two endpoints i0 and i1.
  24. -- These form a de Morgan algebra.
  25. I : Pretype
  26. {-# PRIMITIVE Interval I #-}
  27. i0, i1 : I
  28. {-# PRIMITIVE i0 #-}
  29. {-# PRIMITIVE i1 #-}
  30. -- "minimum" on the interval
  31. iand : I -> I -> I
  32. {-# PRIMITIVE iand #-}
  33. -- "maximum" on the interval.
  34. ior : I -> I -> I
  35. {-# PRIMITIVE ior #-}
  36. -- The interpretation of iand as min and ior as max justifies the fact that
  37. -- ior i (inot i) != i1, since that equality only holds for the endpoints.
  38. -- inot i = 1 - i is a de Morgan involution.
  39. inot : I -> I
  40. {-# PRIMITIVE inot #-}
  41. -- Paths
  42. --------
  43. -- Since every function in type theory is internally continuous,
  44. -- and the two endpoints i0 and i1 are equal, we can take the type of
  45. -- equalities to be continuous functions out of the interval.
  46. -- That is, x ≡ y iff. ∃ f : I -> A, f i0 = x, f i1 = y.
  47. -- The type PathP generalises this to dependent products (i : I) -> A i.
  48. PathP : (A : I -> Type) -> A i0 -> A i1 -> Type
  49. {-# PRIMITIVE PathP #-}
  50. -- By taking the first argument to be constant we get the equality type
  51. -- Path.
  52. Path : {A : Type} -> A -> A -> Type
  53. Path {A} = PathP (\i -> A)
  54. -- reflexivity is given by constant paths
  55. refl : {A : Type} {x : A} -> Path x x
  56. refl {A} {x} i = x
  57. -- Symmetry (for dpeendent paths) is given by inverting the argument to the path, such that
  58. -- sym p i0 = p (inot i0) = p i1
  59. -- sym p i1 = p (inot i1) = p i0
  60. -- This has the correct endpoints.
  61. sym : {A : I -> Type} {x : A i0} {y : A i1} -> PathP A x y -> PathP (\i -> A (inot i)) y x
  62. sym p i = p (inot i)
  63. id : {A : Type} -> A -> A
  64. id x = x
  65. the : (A : Type) -> A -> A
  66. the A x = x
  67. -- The eliminator for the interval says that if you have x : A i0 and y : A i1,
  68. -- and x ≡ y, then you can get a proof A i for every element of the interval.
  69. iElim : {A : I -> Type} {x : A i0} {y : A i1} -> PathP A x y -> (i : I) -> A i
  70. iElim p i = p i
  71. -- This corresponds to the elimination principle for the HIT
  72. -- data I : Pretype where
  73. -- i0 i1 : I
  74. -- seg : i0 ≡ i1
  75. -- The singleton subtype of A at x is the type of elements of y which
  76. -- are equal to x.
  77. Singl : (A : Type) -> A -> Type
  78. Singl A x = (y : A) * Path x y
  79. -- Contractible types are those for which there exists an element to which
  80. -- all others are equal.
  81. isContr : Type -> Type
  82. isContr A = (x : A) * ((y : A) -> Path x y)
  83. -- Using the connection \i j -> y.2 (iand i j), we can prove that
  84. -- singletons are contracible. Together with transport later on,
  85. -- we get the J elimination principle of paths.
  86. singContr : {A : Type} {a : A} -> isContr (Singl A a)
  87. singContr {A} {a} = ((a, \i -> a), \y i -> (y.2 i, \j -> y.2 (iand i j)))
  88. -- Some more operations on paths. By rearranging parentheses we get a
  89. -- proof that the images of equal elements are themselves equal.
  90. cong : {A : Type} {B : A -> Type} (f : (x : A) -> B x) {x : A} {y : A} (p : Path x y) -> PathP (\i -> B (p i)) (f x) (f y)
  91. cong f p i = f (p i)
  92. -- These satisfy definitional equalities, like congComp and congId, which are
  93. -- propositional in vanilla MLTT.
  94. congComp : {A : Type} {B : Type} {C : Type}
  95. {f : A -> B} {g : B -> C} {x : A} {y : A}
  96. (p : Path x y)
  97. -> Path (cong g (cong f p)) (cong (\x -> g (f x)) p)
  98. congComp p = refl
  99. congId : {A : Type} {x : A} {y : A}
  100. (p : Path x y)
  101. -> Path (cong (id {A}) p) p
  102. congId p = refl
  103. -- Just like rearranging parentheses gives us cong, swapping the value
  104. -- and interval binders gives us function extensionality.
  105. funext : {A : Type} {B : A -> Type} {f : (x : A) -> B x} {g : (x : A) -> B x}
  106. (h : (x : A) -> Path (f x) (g x))
  107. -> Path f g
  108. funext h i x = h x i
  109. happly : {A : Type} {B : A -> Type} {f : (x : A) -> B x} {g : (x : A) -> B x}
  110. -> (p : Path f g) -> (x : A) -> Path (f x) (g x)
  111. happly p x i = p i x
  112. -- The proposition associated with an element of the interval
  113. -------------------------------------------------------------
  114. -- Associated with every element i : I of the interval, we have the type
  115. -- IsOne i which is inhabited only when i = i1. In the model, this
  116. -- corresponds to the map [φ] from the interval cubical set to the
  117. -- subobject classifier.
  118. IsOne : I -> Pretype
  119. {-# PRIMITIVE IsOne #-}
  120. -- The value itIs1 witnesses the fact that i1 = i1.
  121. itIs1 : IsOne i1
  122. -- Furthermore, if either of i or j are one, then so is (i or j).
  123. isOneL : {i : I} {j : I} -> IsOne i -> IsOne (ior i j)
  124. isOneR : {i : I} {j : I} -> IsOne j -> IsOne (ior i j)
  125. {-# PRIMITIVE itIs1 #-}
  126. {-# PRIMITIVE isOneL #-}
  127. {-# PRIMITIVE isOneR #-}
  128. -- Partial elements
  129. -------------------
  130. --
  131. -- Since a function I -> A has two endpoints, and a function I -> I -> A
  132. -- has four endpoints + four functions I -> A as "sides" (obtained by
  133. -- varying argument while holding the other as a bound variable), we
  134. -- refer to elements of I^n -> A as "cubes".
  135. -- This justifies the existence of partial elements, which are, as the
  136. -- name implies, partial cubes. Namely, a Partial φ A is an element of A
  137. -- which depends on a proof that IsOne φ.
  138. Partial : I -> Type -> Pretype
  139. {-# PRIMITIVE Partial #-}
  140. -- There is also a dependent version where the type A is itself a
  141. -- partial element.
  142. PartialP : (phi : I) -> Partial phi Type -> Pretype
  143. {-# PRIMITIVE PartialP #-}
  144. -- Why is Partial φ A not just defined as φ -> A? The difference is that
  145. -- Partial φ A has an internal representation which definitionally relates
  146. -- any two partial elements which "agree everywhere", that is, have
  147. -- equivalent values for every possible assignment of variables which
  148. -- makes IsOne φ hold.
  149. -- Cubical Subtypes
  150. --------------------
  151. -- Given A : Type, phi : I, and a partial element u : A defined on φ,
  152. -- we have the type Sub A phi u, notated A[phi -> u] in the output of
  153. -- the type checker, whose elements are "extensions" of u.
  154. -- That is, element of A[phi -> u] is an element of A defined everywhere
  155. -- (a total element), which, when IsOne φ, agrees with u.
  156. Sub : (A : Type) (phi : I) -> Partial phi A -> Pretype
  157. {-# PRIMITIVE Sub #-}
  158. -- Every total element u : A can be made partial on φ by ignoring the
  159. -- constraint. Furthermore, this "totally partial" element agrees with
  160. -- the original total element on φ.
  161. inS : {A : Type} {phi : I} (u : A) -> Sub A phi (\x -> u)
  162. {-# PRIMITIVE inS #-}
  163. -- When IsOne φ, outS {A} {φ} {u} x reduces to u itIs1.
  164. -- This implements the fact that x agrees with u on φ.
  165. outS : {A : Type} {phi : I} {u : Partial phi A} -> Sub A phi u -> A
  166. {-# PRIMITIVE outS #-}
  167. -- The composition operation
  168. ----------------------------
  169. -- Now that we have syntax for specifying partial cubes,
  170. -- and specifying that an element agrees with a partial cube,
  171. -- we can describe the composition operation.
  172. comp : (A : I -> Type) {phi : I} (u : (i : I) -> Partial phi (A i)) -> Sub (A i0) phi (u i0) -> A i1
  173. {-# PRIMITIVE comp #-}
  174. -- In particular, when φ is a disjunction of the form
  175. -- (j = 0) || (j = 1), we can draw u as being a pair of lines forming a
  176. -- "tube", an open square with no floor or roof:
  177. --
  178. -- Given u = \j [ (i = i0) -> x, (i = i1) -> q j] on the extent i || ~i,
  179. -- we draw:
  180. --
  181. -- x q i1
  182. -- | |
  183. -- \j -> x | | \j -> q j
  184. -- | |
  185. -- x q i0
  186. --
  187. -- The composition operation says that, as long as we can provide a
  188. -- "floor" connecting x -- q i0, as a total element of A which, on
  189. -- phi, extends u i0, then we get the "roof" connecting x and q i1
  190. -- for free.
  191. --
  192. -- If we have a path p : x ≡ y, and q : y ≡ z, then we do get the
  193. -- "floor", and composition gets us the dotted line:
  194. --
  195. -- x..........z
  196. -- | |
  197. -- x | | q j
  198. -- | |
  199. -- x----------y
  200. -- p i
  201. trans : {A : Type} {x : A} {y : A} {z : A} -> PathP (\i -> A) x y -> PathP (\i -> A) y z -> PathP (\i -> A) x z
  202. trans {A} {x} p q i =
  203. comp (\i -> A)
  204. {ior i (inot i)}
  205. (\j [ (i = i0) -> x, (i = i1) -> q j ])
  206. (inS (p i))
  207. -- In particular when the formula φ = i0 we get the "opposite face" to a
  208. -- single point, which corresponds to transport.
  209. transp : (A : I -> Type) (x : A i0) -> A i1
  210. transp A x = comp A (\i [ ]) (inS x)
  211. -- Since we have the iand operator, we can also derive the *filler* of a cube,
  212. -- which connects the given face and the output of composition.
  213. fill : (A : I -> Type) {phi : I} (u : (i : I) -> Partial phi (A i)) -> Sub (A i0) phi (u i0) -> (i : I) -> A i
  214. fill A {phi} u a0 i =
  215. comp (\j -> A (iand i j))
  216. {ior phi (inot i)}
  217. (\j [ (phi = i1) as p -> u (iand i j) p, (i = i0) -> outS a0 ])
  218. (inS (outS a0))
  219. hfill : {A : Type} {phi : I} (u : (i : I) -> Partial phi A) -> Sub A phi (u i0) -> I -> A
  220. hfill {A} {phi} u a0 i = fill (\i -> A) {phi} u a0 i
  221. hcomp : {A : Type} {phi : I} (u : (i : I) -> Partial phi A) -> Sub A phi (u i0) -> A
  222. hcomp {A} {phi} u a0 = comp (\i -> A) {phi} u a0
  223. -- For instance, the filler of the previous composition square
  224. -- tells us that trans p refl = p:
  225. transRefl : {A : Type} {x : A} {y : A} (p : Path x y) -> Path (trans p refl) p
  226. transRefl p j i = fill (\i -> A) {ior i (inot i)} (\k [ (i = i0) -> x, (i = i1) -> y ]) (inS (p i)) (inot j)
  227. -- Reduction of composition
  228. ---------------------------
  229. --
  230. -- Composition reduces on the structure of the family A : I -> Type to create
  231. -- the element a1 : (A i1)[phi -> u i1].
  232. --
  233. -- For instance, when filling a cube of functions, the behaviour is to
  234. -- first transport backwards along the domain, apply the function, then
  235. -- forwards along the codomain.
  236. transpFun : {A : Type} {B : Type} {C : Type} {D : Type} (p : Path A B) (q : Path C D)
  237. -> (f : A -> C) -> Path (transp (\i -> p i -> q i) f)
  238. (\x -> transp (\i -> q i) (f (transp (\i -> p (inot i)) x)))
  239. transpFun p q f = refl
  240. -- When considering the more general case of a composition respecing sides,
  241. -- the outer transport becomes a composition.
  242. -- Glueing and Univalence
  243. -------------------------
  244. -- First, let's get some definitions out of the way.
  245. --
  246. -- The *fiber* of a function f : A -> B at a point y : B is the type of
  247. -- inputs x : A which f takes to y, that is, for which there exists a
  248. -- path f(x) = y.
  249. fiber : {A : Type} {B : Type} -> (A -> B) -> B -> Type
  250. fiber f y = (x : A) * Path (f x) y
  251. -- An *equivalence* is a function where every fiber is contractible.
  252. -- That is, for every point in the codomain y : B, there is exactly one
  253. -- point in the input which f maps to y.
  254. isEquiv : {A : Type} {B : Type} -> (A -> B) -> Type
  255. isEquiv {A} {B} f = (y : B) -> isContr (fiber f y)
  256. -- By extracting this point, which must exist because the fiber is contractible,
  257. -- we can get an inverse of f:
  258. inverse : {A : Type} {B : Type} {f : A -> B} -> isEquiv f -> B -> A
  259. inverse eqv y = (eqv y) .1 .1
  260. -- We can prove that «inverse eqv» is a section of f:
  261. section : {A : Type} {B : Type} (f : A -> B) (eqv : isEquiv f) -> Path (\x -> f (inverse eqv x)) id
  262. section f eqv i y = (eqv y) .1 .2 i
  263. -- Proving that it's also a retraction is left as an exercise to the
  264. -- reader. We can package together a function and a proof that it's an
  265. -- equivalence to get a capital-E Equivalence.
  266. Equiv : (A : Type) (B : Type) -> Type
  267. Equiv A B = (f : A -> B) * isEquiv f
  268. -- The identity function is an equivalence between any type A and
  269. -- itself.
  270. idEquiv : {A : Type} -> isEquiv (id {A})
  271. idEquiv y = ((y, \i -> y), \u i -> (u.2 (inot i), \j -> u.2 (ior (inot i) j)))
  272. -- The glue operation expresses that "extensibility is invariant under
  273. -- equivalence". Less concisely, the Glue type and its constructor,
  274. -- glue, let us extend a partial element of a partial type to a total
  275. -- element of a total type, by "gluing" the partial type T using a
  276. -- partial equivalence e onto a total type A.
  277. -- In particular, we have that when φ = i1, Glue A [i1 -> (T, f)] = T.
  278. primGlue : (A : Type) {phi : I}
  279. (T : Partial phi Type)
  280. (e : PartialP phi (\o -> Equiv (T o) A))
  281. -> Type
  282. {-# PRIMITIVE Glue primGlue #-}
  283. -- The glue constructor extends the partial element t : T to a total
  284. -- element of Glue A [φ -> (T, e)] as long as we have a total im : A
  285. -- which is the image of f(t).
  286. --
  287. -- Agreeing with the condition that Glue A [i1 -> (T, e)] = T,
  288. -- we have that glue {A} {i1} t im => t.
  289. prim'glue : {A : Type} {phi : I} {T : Partial phi Type} {e : PartialP phi (\o -> Equiv (T o) A)}
  290. -> (t : PartialP phi T)
  291. -> (im : Sub A phi (\o -> (e o).1 (t o)))
  292. -> primGlue A T e
  293. {-# PRIMITIVE glue prim'glue #-}
  294. -- The unglue operation undoes a glueing. Since when φ = i1,
  295. -- Glue A [φ -> (T, f)] = T, the argument to primUnglue {A} {i1} ...
  296. -- will have type T, and so to get back an A we need to apply the
  297. -- partial equivalence f (defined everywhere).
  298. primUnglue : {A : Type} {phi : I} {T : Partial phi Type} {e : PartialP phi (\o -> Equiv (T o) A)}
  299. -> primGlue A {phi} T e -> A
  300. {-# PRIMITIVE unglue primUnglue #-}
  301. -- Diagramatically, i : I |- Glue A [(i \/ ~i) -> (T, e)] can be drawn
  302. -- as giving us the dotted line in:
  303. --
  304. -- T i0 ......... T i1
  305. -- | |
  306. -- | |
  307. -- e i0 |~ ~| e i1
  308. -- | |
  309. -- | |
  310. -- A i0 --------- A i1
  311. -- A
  312. --
  313. -- Where the the two "e" sides are equivalences, and the bottom side is
  314. -- the line i : I |- A.
  315. --
  316. -- Thus, by choosing a base type, a set of partial types and partial
  317. -- equivalences, we can make a line between two types (T i0) and (T i1).
  318. Glue : (A : Type) {phi : I} -> Partial phi ((X : Type) * Equiv X A) -> Type
  319. Glue A {phi} u = primGlue A {phi} (\o -> (u o).1) (\o -> (u o).2)
  320. -- For example, we can glue together the type A and the type B as long
  321. -- as there exists an Equiv A B.
  322. --
  323. -- A ............ B
  324. -- | |
  325. -- | |
  326. -- equiv |~ ua equiv ~| idEquiv {B}
  327. -- | |
  328. -- | |
  329. -- B ------------ B
  330. -- \i → B
  331. --
  332. univalence : {A : Type} {B : Type} -> Equiv A B -> Path A B
  333. univalence {A} {B} equiv i =
  334. Glue B (\[ (i = i0) -> (A, equiv),
  335. (i = i1) -> (B, the B, idEquiv {B}) ])
  336. -- The fact that this diagram has 2 filled-in B sides explains the
  337. -- complication in the proof below.
  338. --
  339. -- In particular, the actual behaviour of transp (\i -> univalence f i)
  340. -- (x : A) is not just to apply f x to get a B (the left side), it also
  341. -- needs to:
  342. --
  343. -- * For the bottom side, compose along (\i -> B) (the bottom side)
  344. -- * For the right side, apply the inverse of the identity, which
  345. -- is just identity, to get *some* b : B
  346. --
  347. -- But that b : B might not agree with the sides of the composition
  348. -- operation in a more general case, so it composes along (\i -> B)
  349. -- *again*!
  350. --
  351. -- Thus the proof: a simple cubical argument suffices, since
  352. -- for any composition, its filler connects either endpoints. So
  353. -- we need to come up with a filler for the bottom and right faces.
  354. univalenceBeta : {A : Type} {B : Type} (f : Equiv A B) -> Path (transp (\i -> univalence f i)) f.1
  355. univalenceBeta {A} {B} f i a =
  356. let
  357. -- The bottom left corner
  358. botLeft : B
  359. botLeft = transp (\i -> B) (f.1 a)
  360. -- The "bottom face" filler connects the bottom-left corner, f.1 a,
  361. -- and the bottom-right corner, which is the transport of f.1 a
  362. -- along \i -> B.
  363. botFace : Path (f.1 a) botLeft
  364. botFace i = fill (\i -> B) (\j []) (inS (f.1 a)) i
  365. -- The "right face" filler connects the bottom-right corner and the
  366. -- upper-right corner, which is again a simple transport along
  367. -- \i -> B.
  368. rightFace : Path (transp (\i -> B) botLeft) botLeft
  369. rightFace i = fill (\i -> B) (\j []) (inS botLeft) (inot i)
  370. -- The goal is a path between the bottom-left and top-right corners,
  371. -- which we can get by composing (in the path sense) the bottom and
  372. -- right faces.
  373. goal : Path (transp (\i -> B) botLeft) (f.1 a)
  374. goal = trans rightFace (\i -> botFace (inot i))
  375. in goal i
  376. -- The terms univalence + univalenceBeta suffice to prove the "full"
  377. -- univalence axiom of Voevodsky, as can be seen in the paper
  378. --
  379. -- Ian Orton, & Andrew M. Pitts. (2017). Decomposing the Univalence Axiom.
  380. --
  381. -- Available freely here: https://arxiv.org/abs/1712.04890v3
  382. J : {A : Type} {x : A}
  383. (P : (y : A) -> Path x y -> Type)
  384. (d : P x (\i -> x))
  385. {y : A} (p : Path x y)
  386. -> P y p
  387. J P d p = transp (\i -> P (p i) (\j -> p (iand i j))) d
  388. -- Isomorphisms
  389. ---------------
  390. --
  391. -- Since isomorphisms are a much more convenient notion of equivalence
  392. -- than contractible fibers, it's natural to ask why the CCHM paper, and
  393. -- this implementation following that, decided on the latter for our
  394. -- definition of equivalence.
  395. isIso : {A : Type} -> {B : Type} -> (A -> B) -> Type
  396. isIso {A} {B} f = (g : B -> A) * ((y : B) -> Path (f (g y)) y) * ((x : A) -> Path (g (f x)) x)
  397. -- The reason is that the family of types IsIso is not a proposition!
  398. -- This means that there can be more than one way for a function to be
  399. -- an equivalence. This is Lemma 4.1.1 of the HoTT book.
  400. Iso : Type -> Type -> Type
  401. Iso A B = (f : A -> B) * isIso f
  402. -- Nevertheless, we can prove that any function with an isomorphism
  403. -- structure has contractible fibers, using a cubical argument adapted
  404. -- from CCHM's implementation of cubical type theory:
  405. --
  406. -- https://github.com/mortberg/cubicaltt/blob/master/experiments/isoToEquiv.ctt#L7-L55
  407. IsoToEquiv : {A : Type} {B : Type} -> Iso A B -> Equiv A B
  408. IsoToEquiv {A} {B} iso =
  409. let
  410. f = iso.1
  411. g = iso.2.1
  412. s = iso.2.2.1
  413. t = iso.2.2.2
  414. lemIso : (y : B) (x0 : A) (x1 : A) (p0 : Path (f x0) y) (p1 : Path (f x1) y)
  415. -> PathP (\i -> fiber f y) (x0, p0) (x1, p1)
  416. lemIso y x0 x1 p0 p1 =
  417. let
  418. rem0 : Path x0 (g y)
  419. rem0 i = hcomp {A} (\k [ (i = i0) -> t x0 k, (i = i1) -> g y ]) (inS (g (p0 i)))
  420. rem1 : Path x1 (g y)
  421. rem1 i = hcomp {A} (\k [ (i = i0) -> t x1 k, (i = i1) -> g y ]) (inS (g (p1 i)))
  422. p : Path x0 x1
  423. p i = hcomp {A} (\k [ (i = i0) -> rem0 (inot k), (i = i1) -> rem1 (inot k) ]) (inS (g y))
  424. fill0 : I -> I -> A
  425. fill0 i j = hcomp {A} (\k [ (i = i0) -> t x0 (iand j k)
  426. , (i = i1) -> g y
  427. , (j = i0) -> g (p0 i)
  428. ])
  429. (inS (g (p0 i)))
  430. fill1 : I -> I -> A
  431. fill1 i j = hcomp {A} (\k [ (i = i0) -> t x1 (iand j k)
  432. , (i = i1) -> g y
  433. , (j = i0) -> g (p1 i) ])
  434. (inS (g (p1 i)))
  435. fill2 : I -> I -> A
  436. fill2 i j = hcomp {A} (\k [ (i = i0) -> rem0 (ior j (inot k))
  437. , (i = i1) -> rem1 (ior j (inot k))
  438. , (j = i1) -> g y ])
  439. (inS (g y))
  440. sq : I -> I -> A
  441. sq i j = hcomp {A} (\k [ (i = i0) -> fill0 j (inot k)
  442. , (i = i1) -> fill1 j (inot k)
  443. , (j = i1) -> g y
  444. , (j = i0) -> t (p i) (inot k) ])
  445. (inS (fill2 i j))
  446. sq1 : I -> I -> B
  447. sq1 i j = hcomp {B} (\k [ (i = i0) -> s (p0 j) k
  448. , (i = i1) -> s (p1 j) k
  449. , (j = i0) -> s (f (p i)) k
  450. , (j = i1) -> s y k
  451. ])
  452. (inS (f (sq i j)))
  453. in \i -> (p i, \j -> sq1 i j)
  454. fCenter : (y : B) -> fiber f y
  455. fCenter y = (g y, s y)
  456. fIsCenter : (y : B) (w : fiber f y) -> Path (fCenter y) w
  457. fIsCenter y w = lemIso y (fCenter y).1 w.1 (fCenter y).2 w.2
  458. in (f, \y -> (fCenter y, fIsCenter y))
  459. -- We can prove that any involutive function is an isomorphism, since
  460. -- such a function is its own inverse.
  461. involToIso : {A : Type} (f : A -> A) -> ((x : A) -> Path (f (f x)) x) -> isIso f
  462. involToIso {A} f inv = (f, inv, inv)
  463. -- An example of univalence
  464. ---------------------------
  465. --
  466. -- The classic example of univalence is the equivalence
  467. -- not : Bool \simeq Bool.
  468. --
  469. -- We define it here.
  470. Bool : Type
  471. {-# PRIMITIVE Bool #-}
  472. true, false : Bool
  473. {-# PRIMITIVE true #-}
  474. {-# PRIMITIVE false #-}
  475. -- Pattern matching for booleans: If a proposition holds for true and
  476. -- for false, then it holds for every bool.
  477. elimBool : (P : Bool -> Type) -> P true -> P false -> (b : Bool) -> P b
  478. {-# PRIMITIVE if elimBool #-}
  479. -- Non-dependent elimination of booleans
  480. if : {A : Type} -> A -> A -> Bool -> A
  481. if {A} = elimBool (\b -> A)
  482. not : Bool -> Bool
  483. not = if false true
  484. -- By pattern matching it suffices to prove (not (not true)) ≡ true and
  485. -- not (not false) ≡ false. Since not (not true) computes to true (resp.
  486. -- false), both proofs go through by refl.
  487. notInvol : (x : Bool) -> Path (not (not x)) x
  488. notInvol = elimBool (\b -> Path (not (not b)) b) refl refl
  489. notp : Path Bool Bool
  490. notp = univalence (IsoToEquiv (not, involToIso not notInvol))
  491. -- This path actually serves to prove a simple lemma about the universes
  492. -- of HoTT, namely, that any univalent universe is not a 0-type. If we
  493. -- had HITs, we could prove that this fact holds for any n, but for now,
  494. -- proving it's not an h-set is the furthest we can go.
  495. -- First we define what it means for something to be false. In type theory,
  496. -- we take ¬P = P → ⊥, where the bottom type is the only type satisfying
  497. -- the elimination principle
  498. --
  499. -- elimBottom : (P : bottom -> Type) -> (b : bottom) -> P b
  500. --
  501. -- This follows from setting bottom := ∀ A, A.
  502. bottom : Type
  503. bottom = {A : Type} -> A
  504. elimBottom : (P : bottom -> Type) -> (b : bottom) -> P b
  505. elimBottom P x = x
  506. -- We prove that true != false by transporting along the path
  507. --
  508. -- \i -> if (Bool -> Bool) A (p i)
  509. -- (Bool -> Bool) ------------------------------------ A
  510. --
  511. -- To verify that this has the correct endpoints, check out the endpoints
  512. -- for p:
  513. --
  514. -- true ------------------------------------ false
  515. --
  516. -- and evaluate the if at either end.
  517. trueNotFalse : Path true false -> bottom
  518. trueNotFalse p {A} = transp (\i -> if (Bool -> Bool) A (p i)) id
  519. -- To be an h-Set is to have no "higher path information". Alternatively,
  520. --
  521. -- isHSet A = (x : A) (y : A) -> isHProp (Path x y)
  522. --
  523. isHSet : Type -> Type
  524. isHSet A = {x : A} {y : A} (p : Path x y) (q : Path x y) -> Path p q
  525. -- We can prove *a* contradiction (note: this is a direct proof!) by adversarially
  526. -- choosing two paths p, q that we know are not equal. Since "equal" paths have
  527. -- equal behaviour when transporting, we can choose two paths p, q and a point x
  528. -- such that transporting x along p gives a different result from x along q.
  529. --
  530. -- Since transp notp = not but transp refl = id, that's what we go with. The choice
  531. -- of false as the point x is just from the endpoints of trueNotFalse.
  532. universeNotSet : isHSet Type -> bottom
  533. universeNotSet itIs = trueNotFalse (\i -> transp (\j -> itIs notp refl i j) false)