Lecture 2-8: Sets and Higher Types

We saw in Lecture 2-3 that paths in types like Bool, and are equalities between elements. As a consequence, for any two elements x and y, the type of paths x ≡ y is always a proposition — specifically, the proposition that x equals y.

We call types with this property sets. Sets represent the mathematical objects we’re most familiar with from classical mathematics: discrete structures whose equality is unambiguous.

isSet : Type   Type 
isSet A = (x y : A)  isProp (x  y)

As we did for propositions, we’ll spend some time proving closure properties for sets.

Basic Examples

We characterised the types of paths in , Bool and back in Lecture 2-3, and from those characterisations it is easy to show these types are sets.

isSet-⊤ : isSet 
-- Exercise: (Hint: `≡≃≡⊤`)
-- isSet-⊤ x y = isProp-equiv {!!} {!!}

isSet-Bool : isSet Bool
-- Exercise:
-- isSet-Bool x y = {!!}

isSet-ℕ : isSet 
-- Exercise:
-- isSet-ℕ x y = {!!}

The empty type is the set with no elements.

isSet-∅ : isSet 
-- Exercise:
-- isSet-∅ = {!!}

For , we will need a helper that relates paths in the two sides to the characterisation ≡⊎ from back in Lecture 2-3.

isProp-≡⊎ : {A B : Type}  isSet A  isSet B  (a b : A  B)  isProp (a ≡⊎ b)
-- Exercise:
-- isProp-≡⊎ sA sB a b = {!!}

isSet-⊎ : {A B : Type}  isSet A  isSet B  isSet (A  B)
-- Exercise:
-- isSet-⊎ pA pB = {!!}

Every proposition is a set. This may sound like an odd claim, especially if you are used to first-order logic where there is a strict separation between the two. In our setting, any proposition P is a set that has at most one element.

isProp→isSet : isProp A  isSet A
-- Exercise:
-- isProp→isSet pA x y = {!!}

Again, not all types are sets. The type should not be a set since we have a path loop that we know is not the same as refl.

¬isSet-S¹ : ¬ isSet 
-- Exercise:
-- ¬isSet-S¹ isSet = {!!}

The type Type is not a set either. If all paths in Type were equal, then not-Path would be equal to refl as a path Bool ≡ Bool, and this quickly leads to a contradiction.

¬isSet-Type : ¬ isSet Type
-- Exercise: (Hint: `true≢false`)
-- ¬isSet-Type s = {!!}

One way of thinking of isSet is that for any paths p : x ≡ y and q : x ≡ y there is a Square with shape

        refl
    y — — — — > y
    ^           ^            ^
  p |           | q        j |
    |           |            ∙ — >
    x — — — — > x              i
        refl

Of course, we can’t necessarily get some Square like this for any choice of corners x and y, we need the paths p and q to exist to begin with.

So, isSet is like isProp but one dimension higher. We can’t necessarily fill a path between any two points, but we can fill any Square:

isSet→Square : isSet A
   {a b c d : A}
   (r : a  c) (s : b  d)
   (t : a  b) (u : c  d)
   Square t u r s
-- Exercise: (Hint: `toPathP`)
-- isSet→Square sA r s t u = {!!}

Because being a set means that asking that a certain family of types is all propositions, isSet is a proposition about a type.

isProp-isSet : isProp (isSet A)
-- Exercise: (Use `isProp-Π`)
-- isProp-isSet = {!!}

Closure Properties

We can show a number of closure properties of sets like those for propositions and contractible types.

First, if A and B are sets, then A × B is a set, and for A → B it is sufficient for just B to be a set.

isSet-× : isSet A  isSet B  isSet (A × B)
-- Exercise:
-- isSet-× pA pB = {!!}

isSet-→ : isSet B  isSet (A  B)
-- Exercise:
-- isSet-→ pB = {!!}

Similarly to contractible types and propositions, any retract of a set is a set. This follows easily from the following general about fact about homotopies, which is a higher-dimensional generalisation of homotopy-Path. On its own, a homotopy lets us

           g b — — — — — — — > g d
          / ^                 / ^
        /   |               /   |
      /     |             /     |
   g a — — — — — — — > g c      |
    ^       |           ^       |                    ^   j
    |       |           |       |                  k | /
    |       |           |       |                    ∙ — >
    |       |           |       |                      i
    |      f b — — — —  |— — > f d
    |     /             |     /
    |   /               |   /
    | /                 | /
   f a — — — — — — — — f c
homotopy-Square : {f g : A  B}
   (H : (x : A)  (f x  g x))
   {a b c d : A}
   {r : a  c} {s : b  d}
   {t : a  b} {u : c  d}
   Square (ap f t) (ap f u) (ap f r) (ap f s)
   Square (ap g t) (ap g u) (ap g r) (ap g s)
-- Exercise: (Hint: Use an `hcomp` with `sq` as the base and `homotopy-Square` on all sides.)
-- homotopy-Square {B = B} H {r = r} {s} {t} {u} sq i j = {!!}

Aside: Alternatively, produce a path

  Square (ap f t) (ap f u) (ap f r) (ap f s) 
≡ Square (ap g t) (ap g u) (ap g r) (ap g s)

and apply transport.

isSet-retract : B RetractOnto A  isSet B  isSet A
isSet-retract r sB x y p q 
  = homotopy-Square (r .section .proof) (ap-Square (r .map) (sB _ _ _ _))

isSet-equiv : A  B  isSet B  isSet A
isSet-equiv = isSet-retract  equiv→retract

And isSet-equiv lets us easily clear up , without fussing about with defining an observational equality type ≡ℤ:

isSet-ℤ : isSet 
-- Exercise: (Hint: Find a useful equivalence in Lecture 2-2)
-- isSet-ℤ = isSet-equiv {!!} {!!}

Hedberg’s Theorem

There’s a very slick criterion for when a type is a set: Hedberg’s Theorem. Recall the notion of decidable type from the end of Lecture 1-5. A type A is decidable when we have an inhabitant of A ⊎ ¬ A, which we packaged into the Dec inductive type.

Hedberg’s Theorem states that any type with decidable equality is a set. A type has decidable equality whenever the type of paths between any two points is decidable:

Dec≡ : Type   Type 
Dec≡ A = (x y : A)  Dec (x  y)

We are assuming that the path types in A are all decidable, but not that they are decidable propositions. (After all, if we already knew they were propositions then we would already know our type is a set.)

Here’s a simple example. All propositions have decidable equality. Given two elements of a proposition it is easy to decide whether they are equal, the answer is always yes!

isProp→Dec≡ : isProp A  Dec≡ A
-- Exercise:
-- isProp→Dec≡ pA = {!!}

Inductive types often have decidable equality. The proofs are much like the (very similarly named) Dec-≡Bool and Dec-≡ℕ definitions from earlier, which we wrote before we had the notion of path types.

Dec≡-Bool : Dec≡ Bool
-- Exercise:
-- Dec≡-Bool = {!!}

Dec≡-ℕ : Dec≡ 
-- Exercise:
-- Dec≡-ℕ = {!!}

We will prove Hedberg’s Theorem using a slick argument that we learned from Favonia. Here is the idea. Start with a path p : x ≡ y. By assumption, x ≡ y is decidable, so we also have an element of Dec (x ≡ y). This can’t be no, because that would cause a contradiction: after all, we already know that x ≡ y via p.

So we have yes q : Dec (x ≡ y), where q : x ≡ y is also a path between the same points. But here is the key: this path q is the same, regardless of which p we start with.

Start by defining a function that either replaces a path p with the q from Dec (x ≡ y) or derives a contradiction.

Dec≡-bad-replacement : {x y : A}  Dec (x  y)  x  y  x  y
-- Exercise:
-- Dec≡-bad-replacement d p = {!!}

Next, we want to show that this replacement is equal to the path that we started with. The plan is to do this using J, but for that to work we need the replacement to equal refl when p is itself refl. Right now it isn’t: instead, the replacement is just Dec≡-bad-replacement (dec x x) refl : x ≡ x, which doesn’t simplify.

This is easily fixed. Define a function that gives a good replacement path, by composing the old definition with the inverse of the path Dec≡-bad-replacement (dec x x) refl that we want to get rid of:

Dec≡-good-replacement : Dec≡ A  {x y : A}  x  y  x  y
-- Exercise:
-- Dec≡-good-replacement dec {x} {y} p = {!!}

Dec≡-replacement-undo : (dec : Dec≡ A)  {x y : A}  (p : x  y)  Dec≡-good-replacement dec p  p
-- Exercise:
-- Dec≡-replacement-undo dec {x} {y} p = J (λ y p → Dec≡-good-replacement dec p ≡ p) {!!} {!!}

The final lemma is that these good replacements are all equal to each other, regardless of what path you start with. This is easy after pattern matching on Dec (x ≡ y).

Dec≡-replacement-same : (dec : Dec≡ A)  {x y : A}  (p₁ p₂ : x  y)
   Dec≡-good-replacement dec p₁  Dec≡-good-replacement dec p₂
-- Exercise:
-- Dec≡-replacement-same dec {x} {y} p₁ p₂ = {!!}

  where helper : (w : Dec (x  y)) 
            sym (Dec≡-bad-replacement (dec x x) refl)  Dec≡-bad-replacement w p₁
           sym (Dec≡-bad-replacement (dec x x) refl)  Dec≡-bad-replacement w p₂
--      Exercise:
--      helper d = {!!}

Now glue these pieces together to finish the proof.

hedberg : Dec≡ A  isSet A
hedberg dec x y p₁ p₂ =
-- Exercise:
--   p₁                           ≡⟨ {!!} ⟩
--   Dec≡-good-replacement dec p₁ ≡⟨ {!!} ⟩
--   Dec≡-good-replacement dec p₂ ≡⟨ {!!} ⟩
--   p₂ ∎

Higher Types

You may have noticed a pattern developing in the last couple of Lectures. We started with the simplest possible types, contractible types, and have gradually considered types that contain more and more interesting paths.

  • Contractible types contain no information at all. We’ll declare these to have “h-level 0”.
  • Propositions are exactly the types whose path types are contractible, as we checked in isProp→isContr≡ and isContr≡→isProp. We’ll declare these to have “h-level 1”.
  • Sets we are now defining to be the types whose path types are propositions, and so we will say have “h-level 2”.
  • Continuing this way, types with “h-level 3” are those whose path types are sets. We have already seen a nontrivial example: , as we proved in Lecture 2-6.

And so on, with higher and higher types.

isHLevel :   Type   Type 
isHLevel zero    X = isContr X
isHLevel (suc n) X = (x y : X)  isHLevel n (x  y)

The h-level of a type is one way to measure the complexity of its path spaces: a type with a known h-level has all its interesting information below a certain dimension. Inspecting higher dimensional paths eventually reaches path types that are contractible, and all higher dimensional path types from that point on remain contractible (thanks to isContr→isContr≡).

There is a dual way of measuring the complexity of a type that instead specifies that a type has all its interesting paths above a certain dimension. This is known as “connectedness”, and we discuss it further in Lecture 3-2.

Caution: In homotopy theory, a different numbering system is used for h-levels, so that the hierarchy shifted down by two. A set, i.e. a type with h-level 2, is said to have “truncation level 0”, and a proposition has “truncation level -1”. Sometimes this is shortened even further, so that a set is a “0-type” and a proposition is a “(-1)-type”. This is just a difference in conventions, but will be necessary to keep in mind when reading other sources.

Aside: This definition doesn’t exactly match isProp when n = 1, but as we saw in isProp→isContr≡ and isContr≡→isProp, it is equivalent. An alternative definition would specify

isHLevel (suc zero) X = isProp X

directly rather than leaving that to the inductive case. We’ll stick with the simpler definition so that we have one fewer case to deal with in our proofs. It is easy enough to patch over the difference using what we’ve proven already:

isProp→isHLevel1 : isProp A  isHLevel 1 A
isProp→isHLevel1 = isProp→isContr≡

isHLevel1→isProp : isHLevel 1 A  isProp A
isHLevel1→isProp = isContr≡→isProp

isSet→isHLevel2 : isSet A  isHLevel 2 A
isSet→isHLevel2 sA x y = isProp→isHLevel1 (sA x y)

isHLevel2→isSet : isHLevel 2 A  isSet A
isHLevel2→isSet hA x y = isHLevel1→isProp (hA x y)

We can systematise some of the results we’ve seen in this Lecture and the previous. First up, isContr→isProp and isProp→isSet:

isHLevel-suc : (n : )  isHLevel n A  isHLevel (suc n) A
-- Exercise:
-- isHLevel-suc n = {!!}

Next, isProp-isContr, isProp-isProp and isProp-isSet:

isProp-isHLevel : (n : )  isProp (isHLevel n A)
-- Exercise:
-- isProp-isHLevel n = {!!}

Finally, the many closure properties we’ve seen so far. You’ll find isHLevel-equiv useful for both isHLevel-Σ and isHLevel-Π.

isHLevel-≡ : (n : )
   isHLevel n A
   (x y : A)  isHLevel n (x  y)
-- Exercise: (This one is easier than it sounds!)
-- isHLevel-≡ n = {!!}

isHLevel-retract : (n : )  B RetractOnto A  isHLevel n B  isHLevel n A
-- Exercise:
-- isHLevel-retract n = {!!}

isHLevel-equiv : (n : )  (A  B)  isHLevel n B  isHLevel n A
isHLevel-equiv n = isHLevel-retract n  equiv→retract

isHLevel-Σ : {A : Type }  {B : A  Type ℓ'} 
   (n : ) 
   isHLevel n A 
   ((x : A)  isHLevel n (B x))
   isHLevel n (Σ[ x  A ] B x)
-- Exercise:
-- isHLevel-Σ n = {!!}

isHLevel-Π : {B : A  Type }  
   (n : )
   ((x : A)  isHLevel n (B x))
   isHLevel n ((x : A)  B x)
-- Exercise:
-- isHLevel-Π n = {!!}

This is all well and good, but do we have concrete examples of types with a h-level higher than that of sets? We saw in ¬isSet-S¹ that is not a set, but it does have the next h-level beyond that. (Such types are often called “groupoids”.)

All the hard work was done back in Lecture 2-6. We know that base ≡ base is equivalent to , a set, and so has h-level 2. It just takes a little futzing around to show the same is true for any endpoints x and y.

isHLevel3-S¹' : isHLevel 3 
isHLevel3-S¹' = isHLevel-2-x≡y
  where
  isHLevel-2-base≡base : isHLevel 2 (base  base)
  -- Exercise:
  -- isHLevel-2-base≡base = {!!}

  isHLevel-2-base≡y : (y : )  isHLevel 2 (base  y)
  isHLevel-2-base≡y = S¹-ind isHLevel-2-base≡base (isProp→any-PathP  _  isProp-isHLevel 2) _ _)

  isHLevel-2-x≡y : (x y : )  isHLevel 2 (x  y)
  isHLevel-2-x≡y = S¹-ind isHLevel-2-base≡y (isProp→any-PathP  _  isProp-Π λ _  isProp-isHLevel 2) _ _)

References and Further Reading