Two-Level Type Theory
Basics
Two-level type theory (2LTT) refers to versions of Martin-Löf type theory that combine two type theories: one “inner” level that is potentially a homotopy type theory or cubical type theory, which may include univalent universes and higher inductive types, and a second “outer” level that validates uniqueness of identity proofs.
Since version 2.6.2, Agda enables 2LTT with the --two-level
flag. The two levels are distinguished with two hierarchies of universes: the usual universes Set
for the inner level, and a new hierarchy of universes denoted SSet
(for “strict sets”) for the outer level.
Note
The types in SSet
have various names in the literature. They are called non-fibrant types in HTS (2017), outer types in 2LTT (2017), and exo-types in UP (2021). Similarly, these references refer to the types in Set
as fibrant types, inner types, and types, respectively.
Function-types belong to Set
if both their domain and codomain do, and to SSet
otherwise. Records and datatypes can always be declared to belong to SSet
, and can be declared to belong to Set
instead if all their inputs belong to Set
. In particular, any type in Set
has an isomorphic copy in SSet
defined as a trivial record:
record c (A : Set) : SSet where
constructor ↑
field
↓ : A
open c
The main differences between the two levels are that, firstly, homotopical flags such as --without-K
and --cubical
apply only to the Set
level (the SSet
level is never homotopical); and secondly, datatypes belonging to the inner level cannot be pattern-matched on when the motive belongs to the outer level (this is necessary to maintain the previous distinction).
As a primary example, we can define separate inductive equality types for both levels:
infix 4 _≡ˢ_ _≡_
data _≡ˢ_ {a} {A : SSet a} (x : A) : A → SSet a where
reflˢ : x ≡ˢ x
data _≡_ {a} {A : Set a} (x : A) : A → Set a where
refl : x ≡ x
With these definitions, we can prove uniqueness of identity proofs for the strict equality even if --without-K
or --cubical
is enabled:
UIP : {a : Level} {A : SSet a} {x y : A} (p q : x ≡ˢ y) → p ≡ˢ q
UIP reflˢ reflˢ = reflˢ
We can also prove that strictly equal elements are also non-strictly equal:
≡ˢ-to-≡ : {A : Set} {x y : c A} → (x ≡ˢ y) → (↓ x ≡ ↓ y)
≡ˢ-to-≡ reflˢ = refl
The opposite implication, however, fails because, as noted above, we cannot pattern-match against a datatype in Set
when the motive lies in SSet
. Similarly, we can map from the strict natural numbers into the ordinary ones:
data ℕ : Set where
zero : ℕ
succ : ℕ → ℕ
data ℕˢ : SSet where
zeroˢ : ℕˢ
succˢ : ℕˢ → ℕˢ
ℕˢ-to-ℕ : ℕˢ → ℕ
ℕˢ-to-ℕ zeroˢ = zero
ℕˢ-to-ℕ (succˢ n) = succ (ℕˢ-to-ℕ n)
but not vice versa. (Agda does currently allow mapping from the empty SSet
to the empty Set
, but this feature is disputed.)
If the --two-level
flag is combined with --cumulativity
, then each universe Set a
becomes a subtype of SSet a
. In this case we can instead define the coercion c
to be the identity function:
c' : Set → SSet
c' A = A
and replace the coercions ↑
and ↓
with the identity function. However, this combination currently allows some functions to be defined that shouldn’t be allowed; see Agda issue #5761 for details.