Cumulativity

Basics

Since version 2.6.1, Agda supports optional cumulativity of universes under the --cumulativity flag.

  1. {-# OPTIONS --cumulativity #-}

When the --cumulativity flag is enabled, Agda uses the subtyping rule Set i =< Set j whenever i =< j. For example, in addition to its usual type Set, Nat also has the type Set₁ and even Set i for any i : Level.

  1. _ : Set
  2. _ = Nat
  3. _ : Set
  4. _ = Nat
  5. _ : {i} Set i
  6. _ = Nat

With cumulativity is enabled, one can implement lifting to a higher universe as the identity function.

  1. lift : {a b} Set a Set (a b)
  2. lift x = x

Example usage: N-ary functions

In Agda without cumulativity, it is tricky to define a universe-polymorphic N-ary function type A → A → ... → A → B because the universe level depends on whether the number of arguments is zero:

  1. module Without-Cumulativity where
  2. N-ary-level : Level Level Nat Level
  3. N-ary-level ℓ₁ ℓ₂ zero = ℓ₂
  4. N-ary-level ℓ₁ ℓ₂ (suc n) = ℓ₁ N-ary-level ℓ₁ ℓ₂ n
  5. N-ary : {ℓ₁ ℓ₂} n Set ℓ₁ Set ℓ₂ Set (N-ary-level ℓ₁ ℓ₂ n)
  6. N-ary zero A B = B
  7. N-ary (suc n) A B = A N-ary n A B

In contrast, in Agda with cumulativity one can always work with the highest possible universe level. This makes it much easier to define the type of N-ary functions.

  1. module With-Cumulativity where
  2. N-ary : Nat Set ℓ₁ Set ℓ₂ Set (ℓ₁ ℓ₂)
  3. N-ary zero A B = B
  4. N-ary (suc n) A B = A N-ary n A B
  5. curry : (Vec A n B) N-ary n A B
  6. curry {n = zero} f = f []
  7. curry {n = suc n} f = λ x curry λ xs f (x xs)
  8. _$_ : N-ary n A B (Vec A n B)
  9. f $ [] = f
  10. f $ (x xs) = f x $ xs
  11. ∀ⁿ : {A : Set ℓ₁} n N-ary n A (Set ℓ₂) Set (ℓ₁ ℓ₂)
  12. ∀ⁿ zero P = P
  13. ∀ⁿ (suc n) P = x ∀ⁿ n (P x)

Limitations

Currently cumulativity only enables subtyping between universes, but not between any other types containing universes. For example, List Set is not a subtype of List Set₁. Agda also does not have cumulativity for any other types containing universe levels, so List {lzero} Nat is not a subtype of List {lsuc lzero} Nat. Such rules might be added in a future version of Agda.

Constraint solving

When working in Agda with cumulativity, universe level metavariables are often underconstrained. For example, the expression List Nat could mean List {lzero} Nat, but also List {lsuc lzero} Nat, or indeed List {i} Nat for any i : Level.

Currently Agda uses the following heuristic to instantiate universe level metavariables. At the end of each type signature, each mutual block, or declaration that is not part of a mutual block, Agda instantiates all universe level metavariables that are unbounded from above. A metavariable _l : Level is unbounded from above if all unsolved constraints that mention the metavariable are of the form aᵢ =< _l : Level, and _l does not occur in the type of any other unsolved metavariables. For each metavariable that satisfies these conditions, it is instantiated to a₁ ⊔ a₂ ⊔ ... ⊔ aₙ where a₁ =< _l : Level, …, aₙ =< _l : Level are all constraints that mention _l.

The heuristic as described above is considered experimental and is subject to change in future versions of Agda.