module Data.Fin.Finite where

Finite types🔗

The notion of finite set in univalent mathematics is slightly more subtle than even in ordinary constructive mathematics. While we have defined the standard finite sets we can not immediately extend that definition to a proposition is finite” for a type In particular, as is common in constructive mathematics, there are many inequivalent ways that a type can be related to some which all collapse when passing to classical mathematics.

In particular, we say that a set is

  • (Bishop) finite if we have an equivalence

  • Finitely indexed (or Kuratowski finite) if we have a surjection i.e. if is a quotient of a standard finite set;

  • Subfinite if we have an embedding i.e. if is a subset of a standard finite set;

  • Subfinitely indexed if we have a span

    that is if is a quotient of a subfinite set.

While informally we say might say “ is Bishop-finite”, it’s important when doing univalent mathematics to note that all the notions above equip with extra structure. For example, we know that there are equivalences This isn’t an idle concern: if we define the type of finite sets to be, e.g.,

then we end up with something with the incorrect homotopy type! We’d expect the type of finite sets (in a given universe) to be a subtype of the type of sets (in that universe). In particular, we expect the identity type of a finite type to be a set with elements. But the type above is, by univalence, equivalent to the natural numbers:

naïve-fin-is-nat : (Σ[ X  Type ] Σ[ n  Nat ] Fin n  X)  Nat
naïve-fin-is-nat =
  Σ[ X  Type ] Σ[ n  Nat ] Fin n  X ≃⟨ Σ-swap₂ 
  Σ[ n  Nat ] Σ[ X  Type ] Fin n  X ≃⟨ Σ-contract  x  Equiv-is-contr (Fin x)) 
  Nat                                  ≃∎

The fix turns out to be simple. Instead of equipping a type with an equivalence, we record that such an equivalence merely exists. Therefore, we could define the type of finite sets as

truncating the equivalence. However, this definition turns out to be computationally inefficient, especially when we want to show closure properties of finite sets, because of the iterated equivalences. Instead, we’ll define what it means for a type to be finite in terms of lists, which allows us to “flatten” a chain of equivalences.

Listings🔗

A listing for a type consists of a list and a proof that, for each the space is contractible.

record Listing {} (A : Type ) : Type  where
  no-eta-equality
  field
    univ       : List A
    has-member :  a  is-contr (a ∈ₗ univ)

Put another way, a listing of is a list of elements of which contains each at most once, together with an operation which, given an finds an index for it in the list. Any type with a listing is a set: since there are as many proofs of (e.g.) as there are loops and we know there’s exactly one of the former, we conclude that there’s exactly one of the latter. Moreover, since witnesses correspond to fibres of the lookup function over we conclude that any listing generates an equivalence

  find :  a  a ∈ₗ univ
  find a = has-member a .centre

  listing→is-equiv : is-equiv (univ !_)
  listing→is-equiv .is-eqv x = Equiv→is-hlevel 0
    (Σ-ap-snd  x  Equiv.inverse Id≃path) ∙e Equiv.inverse member≃lookup)
    (has-member x)

  index : A  Fin (length univ)
  index = equiv→inverse listing→is-equiv

In particular, any listed type is discrete.

Listing→Discrete : Listing A  Discrete A
Listing→Discrete {A = A} li = go auto where
  module ix = Equiv (listing→equiv-fin li)

  go :  {x y}  Dec (ix.to x  ix.to y)  Dec (x  y)
  go (yes p) = yes (ix.injective p)
  go (no ¬p) = no λ p  ¬p (ap ix.to p)

Basic listed sets🔗

The easiest types we can build listings for are the decidable propositions. If the proposition holds (e.g. with witness then we can take to be a listing. Otherwise, we have the empty listing.

Listing-prop :  _ : Dec A   _ : H-Level A 1   Listing A
Listing-prop  yes a  = record
  { univ       = a  []
  ; has-member = λ where
    a' .centre          here (Id≃path.from prop!)
    a' .paths (here p)  ap here (is-set→is-setᵢ (is-prop→is-set (hlevel 1)) _ _ _ _)
  }
Listing-prop  no ¬a  = record
  { univ       = []
  ; has-member = λ a  absurd (¬a a)
  }

In particular, this is the case for the unit and empty types. We can also build an explicit listing of the booleans, in either order.

instance
  Listing-⊥ : Listing 
  Listing-⊥ = Listing-prop

  Listing-⊤ : Listing 
  Listing-⊤ = Listing-prop

  Listing-Bool : Listing Bool
  Listing-Bool .Listing.univ = true  false  []
  Listing-Bool .Listing.has-member true = record
    { centre = here reflᵢ
    ; paths = λ where
      (here p)          ap here prop!
      (there (here ()))
    }
  Listing-Bool .Listing.has-member false = record
    { centre = there (here reflᵢ)
    ; paths = λ where
      (here ())
      (there (here p))  ap there (ap here prop!)
    }

With a bit more effort, we can also list all the elements of by recursion on the cardinality For the base case, the list is empty, and for a successor, we add the element and shift the universal list for up by 1.

  Listing-Fin :  {n}  Listing (Fin n)
  Listing-Fin {n} = record { univ = all n ; has-member = has } where
    all :  n  List (Fin n)
    all zero    = []
    all (suc n) = fzero  map fsuc (all n)

    mem :  {n} (x : Fin n)  x ∈ₗ all n
    mem x with fin-view x
    ... | zero  = here reflᵢ
    ... | suc i = there (map-member fsuc (mem i))
Proving that the list we’ve built contains every exactly once takes a bit of case analysis, but it’s not complicated.
    abstract
      uniq :  {n} (x : Fin n) (p q : x ∈ₗ all n)  p  q
      uniq x p q with fin-view x
      uniq _ (here p) (here q)  | zero = ap here prop!
      uniq _ (here p) (there q) | zero = absurd (fsuc≠fzero (Id≃path.to (member-map fsuc (all _) q .fst .snd)))
      uniq _ (there p) _        | zero = absurd (fsuc≠fzero (Id≃path.to (member-map fsuc (all _) p .fst .snd)))

      uniq _ (here ()) q         | suc i
      uniq _ (there p) (here ()) | suc i
      uniq {suc n} .(fsuc i) (there p) (there q) | suc i =
        let
          p' : i ∈ₗ all n
          p' = member-map-embedding fsuc fsuc-is-embedding {i} p

          q' : i ∈ₗ all n
          q' = member-map-embedding fsuc fsuc-is-embedding {i} q

          r =
            p                  ≡⟨ sym (member-map-embedding-invl fsuc fsuc-is-embedding {i} p) 
            map-member fsuc p' ≡⟨ ap (map-member fsuc) (uniq {n} i p' q') 
            map-member fsuc q' ≡⟨ member-map-embedding-invl fsuc fsuc-is-embedding {i} q 
            q                  
        in ap there r

    has :  {n} (x : Fin n)  is-contr (x ∈ₗ all n)
    has x .centre = mem x
    has x .paths  = uniq x (mem x)

Closure properties🔗

To show that listed types are closed under operations like we’ll introduce a helper type which allows us to replace the list membership by something specific to the type at hand, and whose h-level we hopefully have an easier time establishing.

The data we require, in addition to the universal list consists of a family of contractible types over and for each a split surjection

record retract→listing {ℓ'} (A : Type ) : Type (  lsuc ℓ') where
  no-eta-equality
  field
    univ   : List A
    member : A  n-Type ℓ' 0

    from  :  {x}  x  member  x ∈ₗ univ
    split :  {x} (e : x ∈ₗ univ)  fibre from e

This is enough to imply that each is contractible, and thus that is a listing of

  has-member :  a  is-contr (a ∈ₗ univ)
  has-member a = record
    { centre = from (member a .is-tr .centre)
    ; paths  = split-surjection→is-hlevel 0 from split (member a .is-tr) .paths
    }

An archetypal use of this helper is showing that listings are closed under dependent sum. For if we have a listing of and, for each a listing of we can concatenate each (tagging it with the corresponding to obtain a list equipped with a split surjection between witnesses and pairs of and

instance
  Listing-Σ
    : {P : A  Type }  _ : Listing A   _ :  {x}  Listing (P x) 
     Listing (Σ A P)
  Listing-Σ {A = A} {P = P}  la   lb  = record { retract→listing mk } where
    mk : retract→listing (Σ A P)
    mk .member (x , y) = record
      { is-tr = ×-is-hlevel 0 (la .has-member x) (lb .has-member y)
      }

    mk .members      = sigma (la .univ)  x  lb {x} .univ)
    mk .from (x , y) = sigma-member x y
    mk .split e      = member-sigma-view (la .univ)  _  lb .univ) e
  Listing-⊎     :  _ : Listing A   _ : Listing B   Listing (A  B)
  Listing-Maybe :  _ : Listing A   Listing (Maybe A)
We can apply the same technique to show that listings are closed under non-dependent sum, and under Maybe.
  Listing-⊎ {A = A} {B = B}  la   lb  = record { retract→listing mk } where
    mk : retract→listing (A  B)
    mk .member (inl x) = record
      { ∣_∣   = Lift (level-of B) (x ∈ₗ la .univ)
      ; is-tr = Lift-is-hlevel 0 (la .has-member x)
      }
    mk .member (inr x) = record
      { ∣_∣   = Lift (level-of A) (x ∈ₗ lb .univ)
      ; is-tr = Lift-is-hlevel 0 (lb .has-member x)
      }

    mk .members = map inl (la .univ) ++ map inr (lb .univ)

    mk .from {inl x} m = ++-memberₗ (map-member inl (m .lower))
    mk .from {inr x} m = ++-memberᵣ (map-member inr (m .lower))

    mk .split {x} e with member-++-view (map inl (la .univ)) (map inr (lb .univ)) e
    mk .split {inl x} _ | inl (i , p) = record
      { snd = ap ++-memberₗ (member-map-embedding-invl inl inl-is-embedding i)  p }
    mk .split {inr x} _ | inr (i , p) = record
      { snd = ap ++-memberᵣ (member-map-embedding-invl inr inr-is-embedding i)  p }
    mk .split {inr x} _ | inl (i , p) = absurd (inl≠inr (Id≃path.to (member-map inl (la .univ) i .fst .snd)))
    mk .split {inl x} _ | inr (i , p) = absurd (inr≠inl (Id≃path.to (member-map inr (lb .univ) i .fst .snd)))

  Listing-Maybe {A = A}  li  = record { retract→listing mk } where
    instance
      s :  {n}  H-Level A (2 + n)
      s = basic-instance 2 (Discrete→is-set (Listing→Discrete li))

    mk : retract→listing (Maybe A)
    mk .members = nothing  map just (li .univ)
    mk .member nothing = el! (Lift (level-of A) )
    mk .member (just x) = record { is-tr = li .has-member x }

    mk .from {nothing} _ = here reflᵢ
    mk .from {just x}  e = there (map-member just e)

    mk .split {nothing} (here p)  = lift tt , ap here prop!
    mk .split {just x}  (here p)  = absurd (just≠nothing (Id≃path.to p))

    mk .split {nothing} (there e) = absurd (just≠nothing (Id≃path.to (member-map just (li .univ) e .fst .snd)))
    mk .split {just x}  (there e) = record { snd = ap there (member-map-embedding-invl just just-is-embedding e) }

We can also show that listings are closed under dependent product, by showing that dependent products over a listed type are equivalent to iterated products over the list.

instance
  Listing-Π : {P : A  Type }  _ : Listing A   _ :  {x}  Listing (P x)   Listing ((x : A)  P x)
  Listing-Π {A = A} {P = P}  la   lb  = li where
    module la = Listing la

    r : retract→listing (Pi la.univ P)
    r .members  = pi la.univ  a  lb {a} .univ)
    r .member p = record
      { ∣_∣   =  h  indexₚ p h ∈ₗ lb .univ
      ; is-tr = Π-is-hlevel 0 λ h  lb .has-member (indexₚ p h)
      }
    r .from  = pi-member' P la.univ  a  lb {a} .univ)
    r .split = member-pi' P la.univ  a  lb {a} .univ)

    li' : Listing (Pi la.univ P)
    li' = record { retract→listing r }

    eqv =
      Pi la.univ P                       ≃⟨ Iso→Equiv (to-pi' P , iso (from-pi' P) (from-to-pi' P) (to-from-pi' P)) 
      ((x : A) (a : x ∈ₗ la.univ)  P x) ≃⟨ Π-cod≃  x  Π-contr-eqv (la.has-member x)) 
      ((x : A)  P x)                    ≃∎

    li : Listing (∀ x  P x)
    li = Equiv→listing eqv li'

Omniscience, exhaustibility, and choice🔗

We now show that listed types are effectively searchable: if we have a decidable predicate over some listed we can either find some which satisfies or prove that no such exists.

Listing→exhaustible
  : {A : Type }  _ : Listing A  (P : A  Type ℓ')  _ :  {x}  Dec (P x) 
   (Σ[ a  A ] P a)  (∀ a  ¬ P a)
Listing→exhaustible {A = A}  li  P  dec  =
  search (li .univ)  a _  li .has-member a .centre) where

We do this by abstracting, then applying induction, over the universal list for To do this, we weaken universality to instead requiring that the we recur over contains every with That way, if we find that an element does not satisfy we can safely remove it from the list.

  search
    : (xs : List A) (f :  a  P a  a ∈ₗ xs)
     (Σ[ a  A ] P a)  (∀ a  ¬ P a)
  search [] f = inr λ i p  case f i p of λ ()
  search (x  xs) ix with dec {x}
  ... | yes Px = inl (x , Px)
  ... | no ¬Px = search xs λ a Pa  case ix a Pa of λ where
    (here a=x)  absurd (¬Px (substᵢ P a=x Pa))
    (there p)   p

This implies that dependent sums and dependent products of decidable families over listed types are decidable.

instance
  Listing→Σ-dec
    : {A : Type }  _ : Listing A  {P : A  Type ℓ'}  _ :  {x}  Dec (P x) 
     Dec (Σ[ a  A ] P a)
  Listing→Σ-dec {P = P} with Listing→exhaustible P
  ... | inl x = yes x
  ... | inr y = no λ (i , p)  y i p

  Listing→Π-dec
    : {A : Type }  _ : Listing A  {P : A  Type ℓ'}  _ :  {x}  Dec (P x) 
     Dec (∀ a  P a)
  Listing→Π-dec {P = P} with Listing→exhaustible (¬_  P)
  ... | inl (i , ¬p) = no λ f  ¬p (f i)
  ... | inr f = yes λ x  dec→dne (f x)

Finally, recalling the equivalence between dependent products over a listed type and iterated products over its universal list, we can also show that any listed type is projective, i.e. that it satisfies choice.

Listing→choice
  :  _ : Listing A  {P : A  Type ℓ'}
   (∀ x   P x )   (∀ x  P x) 

Listing→choice {A = A}  la  {P = P} has = done where
  choose : (xs : List A)   Pi xs P 
  cons   :  {x}   P x    xs   Pi (x  xs) P 

  choose []       = inc tt
  choose (x  xs) = cons (has x) xs

  cons (inc x)        xs = map (x ,_) (choose xs)
  cons (squash x y i) xs = squash (cons x xs) (cons y xs) i

  done = map  f a  to-pi' P f a (la .has-member a .centre))
    (choose (la .univ))

Finiteness🔗

With a strong theory of listed types, we can now define finiteness: A type is finite if it is merely listed. This is a proposition, and (because listed types satisfy choice) inherits all the closure properties above.

Finite : Type   Type 
Finite A =  Listing A 
instance
  Finite-Fin   :  {n}  Finite (Fin n)
  Finite-⊎     :  Finite A    Finite B   Finite (A  B)
  Finite-Maybe :  fa : Finite A   Finite (Maybe A)
  Finite-Σ
    :  {P : A  Type }  _ : Finite A   _ :  {x}  Finite (P x) 
     Finite (Σ A P)
  Finite-Π
    :  {P : A  Type }  _ : Finite A   _ :  {x}  Finite (P x) 
     Finite (∀ a  P a)

  Finite-⊤    : Finite 
  Finite-⊥    : Finite 
  Finite-Bool : Finite Bool

  Finite-Lift :  Finite A   Finite (Lift  A)
  Finite-Coeq
    :   _ : Finite A   _ : Finite B  {f g : A  B}
     Finite (Coeq f g)
  Finite-PathP
    :  {A : I  Type }  s : Finite (A i1)  {x y}
     Finite (PathP A x y)

  Finite-∥-∥ :  _ : Dec A   Finite  A 

Finally, we can define the cardinality of a finite type, not just a listed type, since distinct listings of can differ only in their order (and not their length).

cardinality :  fin : Finite A   Nat
cardinality {A = A}  is  = ∥-∥-rec-set (hlevel 2)  l  length (l .univ)) con is where abstract
  con : (l1 l2 : Listing A)  length (l1 .univ)  length (l2 .univ)
  con l1 l2 = Fin-injective $
    Fin (length (univ l1)) ≃⟨ Listing.listing→fin-equiv l1 
    A                      ≃˘⟨ Listing.listing→fin-equiv l2 ≃˘
    Fin (length (univ l2)) ≃∎