📥 Download raw
{-# OPTIONS --cubical-compatible #-}
module Padova2025.Cubical.Issues.Quotients where

Quotients

open import Padova2025.ProgrammingBasics.Naturals.Base
open import Padova2025.ProgrammingBasics.Naturals.Arithmetic
open import Padova2025.ProvingBasics.Negation
open import Padova2025.ProvingBasics.Equality.Base
open import Padova2025.ProvingBasics.Equality.General
open import Padova2025.ProvingBasics.Equality.NaturalNumbers
open import Padova2025.ProvingBasics.Equality.Reasoning.Core

Let us define the type of integers.

infixl 6 _⊝_
data  : Set where
  _⊝_ :     

The idea is that an integer is represented by a pair, written a ⊝ b, of two natural numbers. We can image a to be credit and b to be debt. For instance, the negative number −2 can be represented as two ⊝ four, or as one ⊝ three.

We can then implement the successor and the predecessor function on the integers:

succℤ :   
succℤ (a  b) = succ a  b
-- increasing credit by one

predℤ :   
predℤ (a  b) = a  succ b
-- increasing debt by one

So far, so good; we could now go on and implement addition, multiplication and so on. However, we would run into a problem when verifying identities, such as succℤ (predℤ x) ≡ x: These aren’t true! While succℤ (predℤ x) represents the same integer as x does, these two values are not really the same. If x = a ⊝ b, then succℤ (predℤ x) is actually succ a ⊝ succ b.

Agda does not know which equivalence relation we have in mind; we have to define it on our own.

infix 4 _≈_
data _≈_ :     Set where
  ≈-refl   : {x : }      x  x
  ≈-sym    : {x y : }    x  y  y  x
  ≈-trans  : {x y z : }  x  y  y  z  x  z
  ≈-cancel : {a b : }    succ a  succ b  a  b
-- the smallest equivalence relation generated by the constructor ≈-cancel

We can then state and prove the mentioned identity as follows.

succ-pred : (x : )  succℤ (predℤ x)  x
succ-pred = {!!}

The type defined above is not really the type of integers; it is the type of integer representations. Each actual integer has infinitely many representations in the type , and we need to take care that we never compare such representations for actual equality _≡_ and instead always use our custom equivalence relation _≈_.

The true integers would arise from the misnamed type by a quotienting operation, where we formally identify values which are related by _≈_, along the following lines:

data ℤ' : Set  -- the actual type of integers
  _⊝_    : ℕ → ℕ → ℤ'
  cancel : (a b : ℕ) → succ a ⊝ succ b ≡ a ⊝ b
-- a dream in standard Agda, reality in Cubical Agda

However, this is not valid Agda code, hinting at a limitation of Agda’s type system: Standard Agda does not have quotient types—only Cubical Agda does.

Without proper quotient types, we have four options, described below but all of them not optimal. We should also remark that designing type-theoretic frameworks with sensible quotient types is an ongoing area of research with several intriguing projects, such as Cubical/Homotopy Type Theory, observational type theory, setoid type theory—and of course the minimalist foundation developed at the University of Padova.

Sticking to normal forms

Write.

Switching to an entirely different implementation

We can sidestep all issues with quotients by implementing the integers in a way with unique representations, for instance as follows.

data ℤ'' : Set where
  pos     :   ℤ''
  negsucc :   ℤ''

The idea is that pos x denotes the integer +x while negsucc x denotes the integer -(succ x).

Of course, this option of switching implementations is not available in general.

Postulating quotients

What Standard Agda does not have, we can postulate:

module PostulatedQuotient
  (X : Set)
  (_≈_ : X  X  Set)
  (≈-refl   : {x : X}      x  x)
  (≈-sym    : {x y : X}    x  y  y  x)
  (≈-trans  : {x y z : X}  x  y  y  z  x  z) where
  postulate
    Q           : Set
    [_]         : X  Q
    Q-eq        : {x y : X}  x  y  [ x ]  [ y ]
    Q-effective : {x y : X}  [ x ]  [ y ]  x  y
    elim
      : {R : Set}  (f : X  R)  (R-eq : {x y : X}  x  y  f x  f y)
       (Q  R)
    elim-β
      : {R : Set}  (f : X  R)  (R-eq : {x y : X}  x  y  f x  f y)
       (x : X)  elim f R-eq [ x ]  f x
    -- and then also an induction principle for predicates Q → Set

open PostulatedQuotient  _≈_ ≈-refl ≈-sym ≈-trans renaming (Q to ℤ''')

This approach works nicely if one only wants to use Agda as a language to describe what’s true in the platonic heaven of blackboard mathematics. But, as already described when we postulated function extensionality, postulates destroy desirable type-theoretic properties. Running proofs may get stuck on postulated terms, and proofs are cluttered by applications of elim-β. In a type system with native support for quotients, such as cubical type theory, (the equivalent of) elim f R-eq [ x ] would simply reduce to f x; instead of elim-β, we could have refl.

Lean is often said to natively support quotients, unlike Standard Agda and Rocq, but its support comes with a heavy price.

Entering setoid hell

An alternative course in Standard Agda is to enter setoid hell. This is the pet name for working with the unquotiented types such as above, but remembering (without tool support) to always prove (again without tool support) that functions preserve the custom equivalence relation.

For instance, in order to ensure that succℤ can be regarded as an function on actual integers instead of just their representations, we would need to prove the following:

succℤ-≈ : {x y : }  x  y  succℤ x  succℤ y
succℤ-≈ = {!!}

This is not too bad, and a similar proof obligation exists in Cubical Agda (which Cubical Agda will automatically remind us of). The proof has mathematical content, ensuring that succℤ doesn’t observably distinguish between equivalent representatives.

The more serious problem is that we need to remember and fulfill such proof obligations also in cases which are mathematically trivial. These cases require uninstructional boilerplate code.

For instance, let us define the double successsor function:

doublesuccℤ :   
doublesuccℤ x = succℤ (succℤ x)

As succℤ preserves the equivalence relation, so does doublesuccℤ. However, we need to explicitly state and prove this fact:

doublesuccℤ-≈ : {x y : }  x  y  doublesuccℤ x  doublesuccℤ y
doublesuccℤ-≈ = {!!}

Exercise: An explicit description of the equivalence relation

Above we have defined _≈_ as the reflexive symmetric transitive hull of the relation given just by the sole constructor ≈-cancel. This approach makes it easy to set up the equivalence relation and to show that elements are related; but showing that elements are not related requires more insight to the situation, such as an explicit description of the generated relation.

_≈'_ :     Set
(a  b) ≈' (a'  b') = (a + b')  (a' + b)
≈'-refl : {x : }  x ≈' x
≈'-refl = {!!}
≈'-sym : {x y : }  x ≈' y  y ≈' x
≈'-sym = {!!}

Beware that the following exercise requires some patience.

≈'-trans : {x y z : }  x ≈' y  y ≈' z  x ≈' z
≈'-trans = {!!}
≈→≈' : {x y : }  x  y  x ≈' y
≈→≈' = {!!}
≈-cancel-more : (x a b : )  (x + a)  (x + b)  a  b
≈-cancel-more = {!!}
≡→≈ : {x y : }  x  y  x  y
≡→≈ = {!!}
≈'→≈ : {x y : }  x ≈' y  x  y
≈'→≈ = {!!}
zeroℤ : 
zeroℤ = zero  zero

oneℤ : 
oneℤ = one  zero
zeroℤ≉oneℤ : ¬ zeroℤ  oneℤ
zeroℤ≉oneℤ = {!!}