open import 1Lab.HLevel.Retracts open import 1Lab.Type.Dec open import 1Lab.HLevel open import 1Lab.Equiv open import 1Lab.Path open import 1Lab.Type module Data.Sum where
Sum Typesπ
Sum types are one of the fundamental ingredients of type theory. They play a dual role to the product type; if products allow us to state that we have elements of two types simultaneously, sum types allow us to state that we have an element of one of two types.
We use the notation A β B
to hint at this typeβs set-theoretic analog: the disjoint union.
infixr 1 _β_ data _β_ {a b} (A : Type a) (B : Type b) : Type (a β b) where inl : A β A β B inr : B β A β B
As warmup, we have that both constructors are embeddings:
inl-inj : {B : Type b} {x y : A} β inl {B = B} x β‘ inl y β x β‘ y inl-inj {A = A} {x = x} path = ap f path where f : A β B β A f (inl x) = x f (inr _) = x inr-inj : {A : Type b} {x y : B} β inr {A = A} x β‘ inr y β x β‘ y inr-inj {B = B} {x = x} path = ap f path where f : A β B β B f (inl _) = x f (inr x) = x β-disjoint : {A : Type a} {B : Type b} {x : A} {y : B} β inl x β‘ inr y β β₯ β-disjoint path = subst (Ξ» { (inl x) β β€ ; (inr x) β β₯ }) path tt
Universal Propertiesπ
One of the most important things about sum types is the following property: given two functions A β C
and B β C
, we can construct a function A β B β C
.
[_,_] : (A β C) β (B β C) β (A β B) β C [ f , g ] (inl x) = f x [ f , g ] (inr x) = g x
Furthermore, this function is βuniversalβ in the following sense: if we have some function h : A β B β C
that behaves like f
when provided an inl a
, and like g
when provided inr b
, then h
must be identical to [ f , g ]
.
[]-unique : β {f : A β C} {g : B β C} {h} β f β‘ h β inl β g β‘ h β inr β [ f , g ] β‘ h []-unique p q = funext Ξ» { (inl x) i β p i x ; (inr x) i β q i x }
We also have the following eta law. In general, eta laws relate the introduction forms with the elimination forms. The most familiar eta law is the one for functions: Ξ» x β (f x)
is the same as f
. In agda, the eta law for functions requires no proof, it holds by definition. However, the same cannot be said for sum types, so we prove it here.
[]-Ξ· : β (x : A β B) β [ inl , inr ] x β‘ x []-Ξ· (inl x) = refl []-Ξ· (inr x) = refl
This universal property can be strengthened to characterising the space of dependent functions out of the disjoint union: A dependent function (x : A β B) β P x
is the product of functions covering the left and right cases.
β-universal : β {A : Type a} {B : Type b} {C : A β B β Type c} β ((x : A β B) β C x) β ( ((x : A) β C (inl x)) Γ ((y : B) β C (inr y))) β-universal {A = A} {B} {P} = IsoβEquiv the-iso where the-iso : Iso _ _
For βsplittingβ a dependent function from the coproduct, we can compose it with either of the constructors to restrict to a function on that factor:
the-iso .fst f = (Ξ» x β f (inl x)) , (Ξ» x β f (inr x))
Similarly, given a pair of functions, we can do a case split on the coproduct to decide which function to apply:
the-iso .snd .is-iso.inv (f , g) (inl x) = f x the-iso .snd .is-iso.inv (f , g) (inr x) = g x the-iso .snd .is-iso.rinv x = refl the-iso .snd .is-iso.linv f i (inl x) = f (inl x) the-iso .snd .is-iso.linv f i (inr x) = f (inr x)
Transformationsπ
Letβs move away from the abstract nonsense of universal properties for a bit, and cleanse our pallate with some small helper functions for mapping between sum types.
β-map : (A β C) β (B β D) β A β B β C β D β-map f g (inl a) = inl (f a) β-map f g (inr b) = inr (g b) β-mapl : (A β C) β A β B β C β B β-mapl f = β-map f id β-mapr : (B β C) β A β B β A β C β-mapr f = β-map id f
Decidablityπ
This type has a very similar structure to Dec, so we provide some helpers to convert between the two.
from-dec : Dec A β A β (A β β₯) from-dec (yes a) = inl a from-dec (no Β¬a) = inr Β¬a to-dec : A β (A β β₯) β Dec A to-dec (inl a) = yes a to-dec (inr Β¬a) = no Β¬a
The proof that these functions are inverses is automatic by computation, and thus it can be shown they are equivalences:
from-dec-is-equiv : {A : Type a} β is-equiv (from-dec {A = A}) from-dec-is-equiv = is-isoβis-equiv (iso to-dec p q) where p : _ p (inl x) = refl p (inr x) = refl q : _ q (yes x) = refl q (no x) = refl
Closure under h-levelsπ
If and are -types, for , then so is their coproduct. The way we prove this is by characterising the entire path space of A β B
in terms of the path spaces for A
and B
, using a recursive definition:
module βPath where Code : A β B β A β B β Type (level-of A β level-of B) Code {B = B} (inl x) (inl y) = Lift (level-of B) (x β‘ y) Code (inl x) (inr y) = Lift _ β₯ Code (inr x) (inl y) = Lift _ β₯ Code {A = A} (inr x) (inr y) = Lift (level-of A) (x β‘ y)
Given a Code for a path in A β B
, we can turn it into a legitimate path. Agda automatically lets us ignore the cases where the Code computes to the empty type.
decode : {x y : A β B} β Code x y β x β‘ y decode {x = inl x} {y = inl xβ} code = ap inl (Lift.lower code) decode {x = inr x} {y = inr xβ} code = ap inr (Lift.lower code)
In the inverse direction, we have a procedure for turning paths into codes:
encode : {x y : A β B} β x β‘ y β Code x y encode {x = inl x} {y = inl y} path = lift (inl-inj path) encode {x = inl x} {y = inr y} path = absurd (β-disjoint path) encode {x = inr x} {y = inl y} path = absurd (β-disjoint (sym path)) encode {x = inr x} {y = inr y} path = lift (inr-inj path)
Now we must establish that encode and decode are inverses. In the one direction, we can use path induction:
decode-encode : {x y : A β B} (p : x β‘ y) β decode (encode p) β‘ p decode-encode = J (Ξ» _ p β decode (encode p) β‘ p) d-e-refl where d-e-refl : {x : A β B} β decode (encode (Ξ» i β x)) β‘ (Ξ» i β x) d-e-refl {x = inl x} = refl d-e-refl {x = inr x} = refl
In the other direction, the proof is by case analysis, and everything computes wonderfully to make the right-hand sides fillable by refl:
encode-decode : {x y : A β B} (p : Code x y) β encode (decode p) β‘ p encode-decode {x = inl x} {y = inl y} p = refl encode-decode {x = inr x} {y = inr y} p = refl
Thus, we have an equivalence between codes for paths in A β B
and actual paths A β B
. Since Code has a nice computational structure, we can establish its h-level by induction:
CodeβPath : {x y : A β B} β Code x y β (x β‘ y) CodeβPath = IsoβEquiv (decode , iso encode decode-encode encode-decode)
open βPath Code-is-hlevel : {x y : A β B} {n : Nat} β is-hlevel A (2 + n) β is-hlevel B (2 + n) β is-hlevel (Code x y) (suc n) Code-is-hlevel {x = inl x} {inl y} {n} ahl bhl = Lift-is-hlevel (suc n) (ahl x y) Code-is-hlevel {x = inr x} {inr y} {n} ahl bhl = Lift-is-hlevel (suc n) (bhl x y)
In the two cases where x
and y
match, we can use the fact that Lift preserves h-levels and the assumption that A
(or B
) have the given h-level.
Code-is-hlevel {x = inl x} {inr y} {n} ahl bhl = Lift-is-hlevel (suc n) (is-propβis-hlevel-suc Ξ» x β absurd x) Code-is-hlevel {x = inr x} {inl y} {n} ahl bhl = Lift-is-hlevel (suc n) (is-propβis-hlevel-suc Ξ» x β absurd x)
In the mismatched cases, we use the fact that propositions have any successor h-level to prove that β₯
is also at the same h-level as A
and B
. Thus, we have:
β-is-hlevel : (n : Nat) β is-hlevel A (2 + n) β is-hlevel B (2 + n) β is-hlevel (A β B) (2 + n) β-is-hlevel n ahl bhl x y = is-hlevelβ (1 + n) CodeβPath (Code-is-hlevel ahl bhl)
Note that, in general, being a proposition and being contractible are not preserved under coproducts. Consider the case where (A, a)
and (B, b)
are both contractible (this generalises to propositions): Then their coproduct has two distinct points, inΒl a
and inr b
. However, the coproduct of disjoint propositions is a proposition:
disjoint-β-is-prop : is-prop A β is-prop B β (A Γ B β β₯) β is-prop (A β B) disjoint-β-is-prop Ap Bp notab (inl x) (inl y) = ap inl (Ap x y) disjoint-β-is-prop Ap Bp notab (inl x) (inr y) = absurd (notab (x , y)) disjoint-β-is-prop Ap Bp notab (inr x) (inl y) = absurd (notab (y , x)) disjoint-β-is-prop Ap Bp notab (inr x) (inr y) = ap inr (Bp x y)
Closure under equivalencesπ
Univalence automatically implies that all type formers respect equivalences. However, the proof using univalence is restricted to types of the same universe level. Thus, β-ap: Coproducts respect equivalences in both arguments, across levels.
β-ap : A β B β C β D β (A β C) β (B β D) β-ap (f , f-eqv) (g , g-eqv) = IsoβEquiv cong where f-iso = is-equivβis-iso f-eqv g-iso = is-equivβis-iso g-eqv cong : Iso _ _ cong .fst (inl x) = inl (f x) cong .fst (inr x) = inr (g x) cong .snd .is-iso.inv (inl x) = inl (f-iso .is-iso.inv x) cong .snd .is-iso.inv (inr x) = inr (g-iso .is-iso.inv x) cong .snd .is-iso.rinv (inl x) = ap inl (f-iso .is-iso.rinv x) cong .snd .is-iso.rinv (inr x) = ap inr (g-iso .is-iso.rinv x) cong .snd .is-iso.linv (inl x) = ap inl (f-iso .is-iso.linv x) cong .snd .is-iso.linv (inr x) = ap inr (g-iso .is-iso.linv x) β-apl : A β B β (A β C) β (B β C) β-apl f = β-ap f (id , id-equiv) β-apr : B β C β (A β B) β (A β C) β-apr f = β-ap (id , id-equiv) f
Algebraic propertiesπ
Considered as an algebraic operator on types, the coproduct satisfies many of the same properties of addition. Specifically, when restricted to finite types, the coproduct is exactly the same as addition.
β-comm : (A β B) β (B β A) β-comm = IsoβEquiv i where i : Iso _ _ i .fst (inl x) = inr x i .fst (inr x) = inl x i .snd .is-iso.inv (inl x) = inr x i .snd .is-iso.inv (inr x) = inl x i .snd .is-iso.rinv (inl x) = refl i .snd .is-iso.rinv (inr x) = refl i .snd .is-iso.linv (inl x) = refl i .snd .is-iso.linv (inr x) = refl β-assoc : ((A β B) β C) β (A β (B β C)) β-assoc = IsoβEquiv i where i : Iso _ _ i .fst (inl (inl x)) = inl x i .fst (inl (inr x)) = inr (inl x) i .fst (inr x) = inr (inr x) i .snd .is-iso.inv (inl x) = inl (inl x) i .snd .is-iso.inv (inr (inl x)) = inl (inr x) i .snd .is-iso.inv (inr (inr x)) = inr x i .snd .is-iso.rinv (inl x) = refl i .snd .is-iso.rinv (inr (inl x)) = refl i .snd .is-iso.rinv (inr (inr x)) = refl i .snd .is-iso.linv (inl (inl x)) = refl i .snd .is-iso.linv (inl (inr x)) = refl i .snd .is-iso.linv (inr x) = refl β-zeror : (A β β₯) β A β-zeror .fst (inl x) = x β-zeror .snd .is-eqv y .centre = inl y , refl β-zeror .snd .is-eqv y .paths (inl x , p) i = inl (p (~ i)) , Ξ» j β p (~ i β¨ j) β-zerol : (β₯ β A) β A β-zerol .fst (inr x) = x β-zerol .snd .is-eqv y .centre = inr y , refl β-zerol .snd .is-eqv y .paths (inr x , p) i = inr (p (~ i)) , Ξ» j β p (~ i β¨ j) β-Γ-distribute : ((A β B) Γ C) β ((A Γ C) β (B Γ C)) β-Γ-distribute = IsoβEquiv i where i : Iso _ _ i .fst (inl x , y) = inl (x , y) i .fst (inr x , y) = inr (x , y) i .snd .is-iso.inv (inl (x , y)) = inl x , y i .snd .is-iso.inv (inr (x , y)) = inr x , y i .snd .is-iso.rinv (inl x) = refl i .snd .is-iso.rinv (inr x) = refl i .snd .is-iso.linv (inl x , _) = refl i .snd .is-iso.linv (inr x , _) = refl