index

⚠️ This module is not part of this project and is only included for reference.
It is either part of the 1lab, the cubical library, or a built-in Agda module.

<!--
```agda
open import 1Lab.HLevel
open import 1Lab.Equiv
open import 1Lab.Path
open import 1Lab.Type

open import Data.Dec.Base
```
-->

```agda
module Data.Sum.Base where
```

# Sum types {defines="sum-type"}

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.

```agda
infixr 3 _⊎_

data _⊎_ {a b} (A : Type a) (B : Type b) : Type (a  b) where
  inl : A  A  B
  inr : B  A  B
```
<!--
```agda
private variable
  a b c d : Level
  A B C D : Type a
```
-->


## 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`.

```agda
[_,_] : (A  C)  (B  C)  (A  B)  C
[ f , g ] (inl x) = f x
[ f , g ] (inr x) = g x
```

<!--
```agda
infix 0 if⁺_then_else_

if⁺_then_else_ : A  B  C  C  C
if⁺ inl _ then y else n = y
if⁺ inr _ then y else n = n
```
-->

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 ]`.

```agda
[]-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.

```agda
[]-η :  (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.

```agda
⊎-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:

```agda
  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:

```agda
  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.

```agda
⊎-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|type of decisions]], so
we provide some helpers to convert between the two.

```agda
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:

```agda
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 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`{.Agda}: Coproducts respect
equivalences in both arguments, across levels.

[Univalence]: 1Lab.Univalence.html#the-axiom

```agda
⊎-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 = ⊎-map f g

  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.

```agda
⊎-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
```