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.

295 lines
10 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 -> Pretype) -> 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 : Pretype} -> A -> A -> Type
  53. Path {A} = PathP (\i -> A)
  54. -- reflexivity is given by constant paths
  55. refl : {A : Pretype} {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 -> Pretype} {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 : Pretype) -> 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 -> Pretype} {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. -- The proposition associated with an element of the interval
  110. -------------------------------------------------------------
  111. -- Associated with every element i : I of the interval, we have the type
  112. -- IsOne i which is inhabited only when i = i1. In the model, this
  113. -- corresponds to the map [φ] from the interval cubical set to the
  114. -- subobject classifier.
  115. IsOne : I -> Pretype
  116. {-# PRIMITIVE IsOne #-}
  117. -- The value itIs1 witnesses the fact that i1 = i1.
  118. itIs1 : IsOne i1
  119. -- Furthermore, if either of i or j are one, then so is (i or j).
  120. isOneL : {i : I} {j : I} -> IsOne i -> IsOne (ior i j)
  121. isOneR : {i : I} {j : I} -> IsOne j -> IsOne (ior i j)
  122. {-# PRIMITIVE itIs1 #-}
  123. {-# PRIMITIVE isOneL #-}
  124. {-# PRIMITIVE isOneR #-}
  125. -- Partial elements
  126. -------------------
  127. --
  128. -- Since a function I -> A has two endpoints, and a function I -> I -> A
  129. -- has four endpoints + four functions I -> A as "sides" (obtained by
  130. -- varying argument while holding the other as a bound variable), we
  131. -- refer to elements of I^n -> A as "cubes".
  132. -- This justifies the existence of partial elements, which are, as the
  133. -- name implies, partial cubes. Namely, a Partial φ A is an element of A
  134. -- which depends on a proof that IsOne φ.
  135. Partial : I -> Type -> Pretype
  136. {-# PRIMITIVE Partial #-}
  137. -- There is also a dependent version where the type A is itself a
  138. -- partial element.
  139. PartialP : (phi : I) -> Partial phi Type -> Pretype
  140. {-# PRIMITIVE PartialP #-}
  141. -- Why is Partial φ A not just defined as φ -> A? The difference is that
  142. -- Partial φ A has an internal representation which definitionally relates
  143. -- any two partial elements which "agree everywhere", that is, have
  144. -- equivalent values for every possible assignment of variables which
  145. -- makes IsOne φ hold.
  146. -- Cubical Subtypes
  147. --------------------
  148. -- Given A : Type, phi : I, and a partial element u : A defined on φ,
  149. -- we have the type Sub A phi u, notated A[phi -> u] in the output of
  150. -- the type checker, whose elements are "extensions" of u.
  151. -- That is, element of A[phi -> u] is an element of A defined everywhere
  152. -- (a total element), which, when IsOne φ, agrees with u.
  153. Sub : (A : Type) (phi : I) -> Partial phi A -> Pretype
  154. {-# PRIMITIVE Sub #-}
  155. -- Every total element u : A can be made partial on φ by ignoring the
  156. -- constraint. Furthermore, this "totally partial" element agrees with
  157. -- the original total element on φ.
  158. inS : {A : Type} {phi : I} (u : A) -> Sub A phi (\x -> u)
  159. {-# PRIMITIVE inS #-}
  160. -- When IsOne φ, outS {A} {φ} {u} x reduces to u itIs1.
  161. -- This implements the fact that x agrees with u on φ.
  162. outS : {A : Type} {phi : I} {u : Partial phi A} -> Sub A phi u -> A
  163. {-# PRIMITIVE outS #-}
  164. -- The composition operation
  165. ----------------------------
  166. -- Now that we have syntax for specifying partial cubes,
  167. -- and specifying that an element agrees with a partial cube,
  168. -- we can describe the composition operation.
  169. comp : (A : I -> Type) {phi : I} (u : (i : I) -> Partial phi (A i)) -> Sub (A i0) phi (u i0) -> A i1
  170. {-# PRIMITIVE comp #-}
  171. -- In particular, when φ is a disjunction of the form
  172. -- (j = 0) || (j = 1), we can draw u as being a pair of lines forming a
  173. -- "tube", an open square with no floor or roof:
  174. --
  175. -- Given u = \j [ (i = i0) -> x, (i = i1) -> q j] on the extent i || ~i,
  176. -- we draw:
  177. --
  178. -- x q i1
  179. -- | |
  180. -- \j -> x | | \j -> q j
  181. -- | |
  182. -- x q i0
  183. --
  184. -- The composition operation says that, as long as we can provide a
  185. -- "floor" connecting x -- q i0, as a total element of A which, on
  186. -- phi, extends u i0, then we get the "roof" connecting x and q i1
  187. -- for free.
  188. --
  189. -- If we have a path p : x ≡ y, and q : y ≡ z, then we do get the
  190. -- "floor", and composition gets us the dotted line:
  191. --
  192. -- x..........z
  193. -- | |
  194. -- x | | q j
  195. -- | |
  196. -- x----------y
  197. -- p i
  198. trans : {A : Type} {x : A} {y : A} {z : A} -> PathP (\i -> A) x y -> PathP (\i -> A) y z -> PathP (\i -> A) x z
  199. trans {A} {x} p q i =
  200. comp (\i -> A)
  201. {ior i (inot i)}
  202. (\j [ (i = i0) -> x, (i = i1) -> q j ])
  203. (inS (p i))
  204. -- In particular when the formula φ = i0 we get the "opposite face" to a
  205. -- single point, which corresponds to transport.
  206. transp : (A : I -> Type) (x : A i0) -> A i1
  207. transp A x = comp A (\i [ ]) (inS x)
  208. -- Since we have the iand operator, we can also derive the *filler* of a cube,
  209. -- which connects the given face and the output of composition.
  210. fill : (A : I -> Type) {phi : I} (u : (i : I) -> Partial phi (A i)) -> Sub (A i0) phi (u i0) -> (i : I) -> A i
  211. fill A {phi} u a0 i =
  212. comp (\j -> A (iand i j))
  213. (\j [ (phi = i1) as p -> u (iand i j) p, (i = i0) -> outS a0 ])
  214. (inS (outS a0))
  215. -- For instance, the filler of the previous composition square
  216. -- tells us that trans p refl = p:
  217. transRefl : {A : Type} {x : A} {y : A} (p : Path x y) -> Path (trans p refl) p
  218. transRefl p j i = fill (\i -> A) {ior i (inot i)} (\k [ (i = i0) -> x, (i = i1) -> y ]) (inS (p i)) (inot j)
  219. -- Reduction of composition
  220. ---------------------------
  221. --
  222. -- Composition reduces on the structure of the family A : I -> Type to create
  223. -- the element a1 : (A i1)[phi -> u i1].
  224. --
  225. -- For instance, when filling a cube of functions, the behaviour is to
  226. -- first transport backwards along the domain, apply the function, then
  227. -- forwards along the codomain.
  228. transpFun : {A : Type} {B : Type} {C : Type} {D : Type} (p : Path A B) (q : Path C D)
  229. -> (f : A -> C) -> Path (transp (\i -> p i -> q i) f)
  230. (\x -> transp (\i -> q i) (f (transp (\i -> p (inot i)) x)))
  231. transpFun p q f = refl
  232. -- When considering the more general case of a composition respecing sides,
  233. -- the outer transport becomes a composition.