[Agda] Univalence via Agda's primTrustMe again

Alan Jeffrey ajeffrey at bell-labs.com
Fri Jan 23 23:53:47 CET 2015


Hi all,

OK, I made another shot at a computational version of univalence, using 
Agda's primTrustMe to get beta-reduction.

In the attached Agda code, there's a construction of the 2-categorical 
version of univalence such that many of the equivalences are 
beta-reductions. In particular:

   ap(ua(e))(a)  beta reduces to  e(a)

I only did the 2-categorical version, which lifts up the skeletal 
equivalence on Set_0 to a non-skeletal equivalence on Set_1. It would be 
interesting to see if it goes through for any Set_n, to get an 
n-categorical model, but this may require more book-keeping than I am 
capable of.

Let ≣ be the skeletal equivalence satisfying K.

The identity type on Set₀ is (a ≡₀ b) whenever (a ≣ b). From this, we 
can define (A ≃₁ B : Set₀) as per usual (half-adjoint equivalences).

The definition of the identity type on Set₁ is (a ≡₁ b : A) whenever:

- p : (a ≣ b)
- q : ∀ C → (C a ≃₁ C b)
- r : ∀ C D → (q (C ∘ D)) ≣ (≃₁-cong C (q D))

from which we can define (A ≃₂ B : Set₁), and show the 2-categorical 
version of univalence:

   (A ≡₁ B) ≃₂ (A ≃₁ B)

The Agda trick to getting beta-reductions is that from:

   trustMe : ∀ (a b : A) → (a ≣ b)

we can build:

   POSTULATE[_↦_] : ∀ (a : A) (b : B a) → (∀ a → B a)
   POSTULATE[ a ↦ b ] a′ with trustMe a a′
   POSTULATE[ a ↦ b ] .a | refl = b

Since trustMe a a beta-reduces to refl, (POSTULATE[ a ↦ b ] a) 
beta-reduces to b. We can use this to make some of the coherence 
conditions definitional equalities, not just propositional ones. For 
example, the congruence property for equivalence can be defined:

   ≃₁-cong : ∀ {A B} C → (A ≃₁ B) → (C A ≃₁ C B)
   ≃₁-cong = POSTULATE[ id ↦ id ]

which means that (≃₁-cong id e) beta-reduces to e. From this, we get 
that ap(ua(e))(a) beta reduces to e(a).

One restriction is that the only beta-reduction on postulates is the one 
given. This is good because we can't introduce nondeterminism, but bad 
because we only get one beta-reduction per term, so we have to choose 
carefully which propositional equality to make definitional.

Thoughts? Is the construction of ≡₁ as a contextual equivalence 
generated by ≃₁ new?

Alan.
-------------- next part --------------
open import Level using (Level ; zero ; suc ; _⊔_ )

module UnivalenceViaPrimTrustMe2 where

-- Function identity and composition

id : ∀ {ℓ} {A : Set ℓ} → A → A
id = λ a → a

_∘_ : ∀ {ℓ} {A B C : Set ℓ} → (B → C) → (A → B) → (A → C)
(f ∘ g) a = f(g a)

-- Skeletal propositional equality

data _≣_ {ℓ} {A : Set ℓ} (a : A) : A → Set ℓ where
  ≣-refl : (a ≣ a)

{-# BUILTIN EQUALITY _≣_ #-}
{-# BUILTIN REFL  ≣-refl #-}

≣-uniq : ∀ {ℓ} {A : Set ℓ} {a b : A} → (p q : a ≣ b) → (p ≣ q)
≣-uniq ≣-refl ≣-refl = ≣-refl -- Requires K

≣-sym : ∀ {ℓ} {A : Set ℓ} {a b : A} → (a ≣ b) → (b ≣ a)
≣-sym ≣-refl = ≣-refl

≣-trans : ∀ {ℓ} {A : Set ℓ} {a b c : A} → (a ≣ b) → (b ≣ c) → (a ≣ c)
≣-trans ≣-refl ≣-refl = ≣-refl

≣-cong : ∀ {ℓ m} {A : Set ℓ} {B : Set m} → (f : A → B) → ∀ {a b} → (a ≣ b) → (f a ≣ f b)
≣-cong f ≣-refl = ≣-refl

≣-cong₂ : ∀ {ℓ m n} {A : Set ℓ} {B : Set m} {C : Set n} → (f : A → B → C) → ∀ {a b c d} → (a ≣ b) → (c ≣ d) → (f a c ≣ f b d)
≣-cong₂ f ≣-refl ≣-refl = ≣-refl

≣-cong-id : ∀ {ℓ} {A : Set ℓ} {a b : A} (p : a ≣ b) → (≣-cong id p ≣ p)
≣-cong-id ≣-refl = ≣-refl

≣-cong-dist-∘ : ∀ {ℓ} {A B C : Set ℓ} (f : B → C) (g : A → B) {a b : A} (p : a ≣ b) →
  (≣-cong (f ∘ g) p) ≣ (≣-cong f (≣-cong g p))
≣-cong-dist-∘ f g ≣-refl = ≣-refl

private 

  -- (trustMe a b) has type (a ≣ b) for any a and b, handle with care!

  primitive primTrustMe : ∀ {ℓ} {A : Set ℓ} {a b : A} → (a ≣ b)

  trustMe : ∀ {ℓ} {A : Set ℓ} (a b : A) → (a ≣ b)
  trustMe a b = primTrustMe

  -- (trustMe a a) beta reduces to ≣-refl
  -- we can use this to postulate a function POSTULATE[ a ↦ b ]
  -- such that (POSTULATE[ a ↦ b ] a) beta reduces to b
  -- Note that there is no way AFAIK to allow more than one beta-reduction
  -- so we can't introduce nondeterminism this way
  
  POSTULATE[_↦_] : ∀ {ℓ m} {A : Set ℓ} {B : A → Set m} (a : A) (b : B a) → (∀ a → B a)
  POSTULATE[ a ↦ b ] a′ with trustMe a a′
  POSTULATE[ a ↦ b ] .a | ≣-refl = b

-- For example, we can introduce dependent function extensionality,
-- in such a way that (≣-dext (λ x → ≣-refl)) beta reduces to ≣-refl

≣-dext : ∀ {ℓ m} {A : Set ℓ} {B : A → Set m} {f g : ∀ a → B a} → (∀ a → f a ≣ g a) → (f ≣ g)
≣-dext {f = f} {g = g} = P g where

  P : ∀ g → (∀ a → f a ≣ g a) → (f ≣ g)
  P = POSTULATE[ f ↦ POSTULATE[ (λ a → ≣-refl) ↦ ≣-refl ] ]

-- (≣-dext (λ x → ≣-refl)) is definitionally equal to ≣-refl

≣-dext-refl : ∀ {ℓ m} {A : Set ℓ} {B : A → Set m} {f : ∀ a → B a} → (≣-dext {f = f} (λ x → ≣-refl)) ≣ ≣-refl
≣-dext-refl = ≣-refl

-- Equality on Set₀ is skeletal

record _≡₀_ {A : Set₀} (a b : A) : Set where
  constructor ⟨_⟩
  field p : (a ≣ b)

≡₀-cong : ∀ {A B} (f : A → B) {a b} → (a ≡₀ b) → (f a ≡₀ f b)
≡₀-cong f ⟨ p ⟩ = ⟨ ≣-cong f p ⟩

≡₀-refl : ∀ {A} {a : A} → (a ≡₀ a)
≡₀-refl = ⟨ ≣-refl ⟩

≡₀-sym : ∀ {A} {a b : A} → (a ≡₀ b) → (b ≡₀ a)
≡₀-sym ⟨ p ⟩ = ⟨ ≣-sym p ⟩

≡₀-trans : ∀ {A} {a b c : A} → (a ≡₀ b) → (b ≡₀ c) → (a ≡₀ c)
≡₀-trans ⟨ p ⟩ ⟨ q ⟩ = ⟨ ≣-trans p q ⟩

≡₀-uniq : ∀ {A} {a b : A} (p q : a ≡₀ b) → (p ≡₀ q)
≡₀-uniq ⟨ p ⟩ ⟨ q ⟩ = ⟨ (≣-cong ⟨_⟩ (≣-uniq p q)) ⟩

-- Equivalence

record _≃₁_ (A B : Set₀) : Set₁ where
  constructor ⟨_,_,_,_,_⟩
  field f : A → B
  field f⁻¹ : B → A
  field η : ∀ a → f⁻¹(f a) ≡₀ a
  field ε : ∀ b → f(f⁻¹ b) ≡₀ b
  field τ : ∀ a → ≡₀-cong f(η a) ≡₀ ε(f a)
open _≃₁_ using () renaming (f to apply₁ ; f⁻¹ to apply₁⁻¹ ; η to apply₁-η ; ε to apply₁-ε ; τ to apply₁-τ )

-- The identity function is an equivalence

≃₁-refl : ∀ {A} → (A ≃₁ A)
≃₁-refl = ⟨ id , id , (λ a → ≡₀-refl) , (λ a → ≡₀-refl) , (λ a → ≡₀-refl) ⟩

-- Swapping preserves equvalence

≃₁-sym : ∀ {A B} → (A ≃₁ B) → (B ≃₁ A)
≃₁-sym ⟨ f , f⁻¹ , η , ε , τ ⟩ = ⟨ f⁻¹ , f , ε , η , σ ⟩ where

  σ : ∀ b → ≡₀-cong f⁻¹ (ε b) ≡₀ η (f⁻¹ b)
  σ b = ≡₀-uniq _ _ -- This is actually provable directly, using the fact that ≣ is skeletal is just lazy

-- Function composition respects equivalence

≃₁-trans : ∀ {A B C} → (A ≃₁ B) → (B ≃₁ C) → (A ≃₁ C)
≃₁-trans ⟨ f , f⁻¹ , η , ε , τ ⟩ ⟨ g , g⁻¹ ,  η′ , ε′ , τ′ ⟩ =
  ⟨ (g ∘ f)
  , (f⁻¹ ∘ g⁻¹)
  , (λ a → ≡₀-trans (≡₀-cong f⁻¹ (η′ (f a))) (η a))
  , (λ b → ≡₀-trans (≡₀-cong g (ε (g⁻¹ b))) (ε′ b))
  , (λ a → ≡₀-uniq _ _) ⟩

-- If (A ≣ B) then (A ≃₁ B)

≣-to-≃₁ : ∀ {A B : Set₀} → (A ≣ B) → (A ≃₁ B)
≣-to-≃₁ ≣-refl = ⟨ id , id , (λ a → ≡₀-refl) , (λ a → ≡₀-refl) , (λ a → ≡₀-refl) ⟩

-- Univalence implies that (A ≃₁ B) implies (A ≣ B)

≃₁-to-≣ : ∀ {A B} → (A ≃₁ B) → (A ≣ B)
≃₁-to-≣ {A} {B} = f B where

  f : ∀ B → (A ≃₁ B) → (A ≣ B)
  f = POSTULATE[ A ↦ POSTULATE[ ≃₁-refl ↦ ≣-refl ] ]

-- ≃₁-to-≣ ≃₁-refl is definitionally equal to ≣-refl

≃₁-to-≣-refl : ∀ A → (≃₁-to-≣ (≃₁-refl {A})) ≣ ≣-refl
≃₁-to-≣-refl A = ≣-refl

-- Univalence implies that ≃₁ is a congruence

≃₁-cong-via-≣ : ∀ {A B} C → (A ≃₁ B) → (C A ≃₁ C B)
≃₁-cong-via-≣ C e = ≣-to-≃₁ (≣-cong C (≃₁-to-≣ e))

-- We postulate a congruence which respects identity

≃₁-cong : ∀ {A B} C → (A ≃₁ B) → (C A ≃₁ C B)
≃₁-cong = POSTULATE[ id ↦ id ]

-- (≃₁-cong id e) is definitionally equal to e

≃₁-cong-id : ∀ {A B} (e : A ≃₁ B) → (≃₁-cong id e) ≣ e
≃₁-cong-id e = ≣-refl

-- We also postulate that ≃₁-cong respects composition, refl, sym and trans
-- but we don't require any β-reductions for these.

postulate ≃₁-cong-∘ : ∀ C D {A B} (e : A ≃₁ B) → (≃₁-cong (C ∘ D) e) ≣ (≃₁-cong C (≃₁-cong D e))
postulate ≃₁-cong-refl : ∀ {A} C → (≃₁-cong C (≃₁-refl {A})) ≣ ≃₁-refl
postulate ≃₁-cong-sym : ∀ F {A B} (e : A ≃₁ B) → (≃₁-cong F (≃₁-sym e)) ≣ (≃₁-sym (≃₁-cong F e))
postulate ≃₁-cong-trans : ∀ F {A B C} (d : A ≃₁ B) (e : B ≃₁ C) → (≃₁-cong F (≃₁-trans d e)) ≣ (≃₁-trans (≃₁-cong F d) (≃₁-cong F e))

-- Unfortunately, this isn't a good computational model of univalence,
-- as it doesn't have "enough" beta-reductions.
-- In particular, (ap₀ (≃₁-to-≣ e) a) does not beta reduce to (apply₁ e a)
-- since (≃₁-to-≣ e) beta reduces to (primTrustMe) and so e
-- has been lost.

-- We fix this by introducing a new type (a ≡₁ b) which keeps track of
-- an equivalence as well as a proof of (a ≣ b). However we do not
-- necessarily have that a and b are sets, they can be of any type. We
-- fix that by using a contextual equivalence: (a ≡₁ b) implies that
-- (C a ≃₁ C b) for any context C.

-- This introduces too many inhabitants of (a ≡₁ b) since 
-- there is nothing relating the witness of (C a ≃₁ C b)
-- with the witness of (D a ≃₁ D b). We fix this by requiring
-- q to be a functor.

-- The idea is that (a ≣ b) is the "skeleton" of (a ≡₁ b), it just
-- records that a and b are equal, not why they are equal.

-- Rather annoyingly, for cardinality reasons we need to keep the proof
-- of (a ≣ b) explicitly. If q could be given the type
-- q : ∀ (C : A → Set (ℓ + 1)) → (C a ≃₁ C b)
-- then we would have apply₁ (q (λ x → (a ≣ x))) ≣-refl
-- as our witness of (a ≣ b).

-- Note that this is just the 2-categorical version, not the n-categorical
-- or ω-categorical version. We could iterate these definitions,
-- so (a ≡₂ b) : Set (ℓ + 2), (a ≡₃ b) : Set (ℓ + 3), ...
-- but there would be some care in doing so, as there are places
-- I've been lazy and used ≣-uniq, and there are probably extra coherence
-- conditions required. In particular there should be a proof
-- that the map from _≡₂_ to _≡₁_ gives the same path as the map from 
-- _≡₂_ to _≃₂_. 

record _≡₁_ {A : Set₁} (a b : A) : Set₁ where
  constructor ⟨_,_,_⟩
  field p : a ≣ b
  field q : ∀ C → (C a ≃₁ C b)
  field r : ∀ C D → (q (C ∘ D)) ≣ (≃₁-cong C (q D))
open _≡₁_ public using () renaming (p to ≡₁-to-≣ ; q to ≡₁-to-≃₁-cong ; r to ≡₁-to-≃₁-cong-∘ )

-- ≣ implies ≡₁ 

≣-to-≡₁ : ∀ {A} {a b : A} → (a ≣ b) → (a ≡₁ b)
≣-to-≡₁ ≣-refl = ⟨ ≣-refl , (λ C → ≃₁-refl) , (λ C D → ≣-sym (≃₁-cong-refl C)) ⟩

-- ≡₁ implies ≃₁
≡₁-to-≃₁ : ∀ {A B} → (A ≡₁ B) → (A ≃₁ B)
≡₁-to-≃₁ p = ≡₁-to-≃₁-cong p id

-- ≃₁ implies ≡₁

≃₁-to-≡₁ : ∀ {A B} → (A ≃₁ B) → (A ≡₁ B)
≃₁-to-≡₁ e = ⟨ (≃₁-to-≣ e) , (λ C → ≃₁-cong C e) , (λ C D → ≃₁-cong-∘ C D e) ⟩

-- ≡₁ is reflexive

≡₁-refl : ∀ {A} {a : A} → (a ≡₁ a)
≡₁-refl = 
  ⟨ ≣-refl
  , (λ C → ≃₁-refl) 
  , (λ C D → ≣-sym (≃₁-cong-refl C)) ⟩

-- ≡₁ is symmetric

≡₁-sym : ∀ {A} {a b : A} → (a ≡₁ b) → (b ≡₁ a)
≡₁-sym ⟨ p , q , r ⟩ = 
  ⟨ ≣-sym p
  , (λ C → ≃₁-sym (q C))
  , (λ C D → ≣-trans (≣-cong ≃₁-sym (r C D)) (≣-sym (≃₁-cong-sym C (q D)))) ⟩

-- ≡₁ is transitive

≡₁-trans : ∀ {A} {a b c : A} → (a ≡₁ b) → (b ≡₁ c) → (a ≡₁ c)
≡₁-trans ⟨ p₁ , q₁ , r₁ ⟩ ⟨ p₂ , q₂ , r₂ ⟩ = 
  ⟨ ≣-trans p₁ p₂
  , (λ C → ≃₁-trans (q₁ C) (q₂ C))
  , (λ C D → ≣-trans (≣-cong₂ ≃₁-trans (r₁ C D) (r₂ C D)) (≣-sym (≃₁-cong-trans C (q₁ D) (q₂ D)))) ⟩

-- ≡₁ is a congruence

≡₁-cong : ∀ {A B} (f : A → B) {a b : A} → (a ≡₁ b) → (f a ≡₁ f b)
≡₁-cong f p = ⟨ ≣-cong f (≡₁-to-≣ p) , (λ C → ≡₁-to-≃₁-cong p (C ∘ f)) , (λ C D → ≡₁-to-≃₁-cong-∘ p C (D ∘ f)) ⟩

-- ap₁ p just extracts the function from p

ap₁ : ∀ {A B} → (A ≡₁ B) → (A → B)
ap₁ p = apply₁ (≡₁-to-≃₁ p)

ap₁⁻¹ : ∀ {A B} → (A ≡₁ B) → (B → A)
ap₁⁻¹ p = apply₁⁻¹ (≡₁-to-≃₁ p)

-- (ap₁ (≃₁-to-≡₁ e)) is definitionally equal to (apply₁ e)

ap₁-β : ∀ {A B} (e : A ≃₁ B) → (ap₁ (≃₁-to-≡₁ e)) ≣ (apply₁ e)
ap₁-β e = ≣-refl

-- ≡₁-to-≃₁ (≃₁-to-≡₁ e) is definitionally equal to p

≡₁-to-≃-ε : ∀ {A B} (e : A ≃₁ B) → (≡₁-to-≃₁ (≃₁-to-≡₁ e)) ≡₁ e
≡₁-to-≃-ε e = ≡₁-refl

-- ≃₁-to-≡₁ (≡₁-to-≃₁ p) is propositionally equal to p

≡₁-to-≃₁-η-lemma : ∀ {A B} (p q : A ≡₁ B) → (q ≣ ≃₁-to-≡₁ (≡₁-to-≃₁ p)) → (q ≡₁ p)
≡₁-to-≃₁-η-lemma p ⟨ a  , b  , c  ⟩ H with ≣-trans (≣-cong ≡₁-to-≣ H) (≣-uniq (≡₁-to-≣ (≃₁-to-≡₁ (≡₁-to-≃₁ p))) (≡₁-to-≣ p))
≡₁-to-≃₁-η-lemma p ⟨ ._ , b  , c  ⟩ H | ≣-refl with ≣-trans (≣-cong ≡₁-to-≃₁-cong H) (≣-dext (λ C → ≣-sym (≡₁-to-≃₁-cong-∘ p C id)))
≡₁-to-≃₁-η-lemma p ⟨ ._ , ._ , c  ⟩ H | ≣-refl | ≣-refl with (≣-dext (λ C → ≣-dext (λ D → ≣-uniq (c C D) (≡₁-to-≃₁-cong-∘ p C D))))
≡₁-to-≃₁-η-lemma p ⟨ ._ , ._ , ._ ⟩ H | ≣-refl | ≣-refl | ≣-refl = ≡₁-refl

≡₁-to-≃₁-η : ∀ {A B} (p : A ≡₁ B) → (≃₁-to-≡₁ (≡₁-to-≃₁ p)) ≡₁ p
≡₁-to-≃₁-η p = ≡₁-to-≃₁-η-lemma p _ ≣-refl

-- These equalities are inverses

≡₁-to-≃₁-τ-lemma :  ∀ {A B} (p q : A ≡₁ B) → (H : q ≣ ≃₁-to-≡₁ (≡₁-to-≃₁ p)) → (≡₁-cong ≡₁-to-≃₁ (≡₁-to-≃₁-η-lemma p q H)) ≡₁ (≡₁-cong ≡₁-to-≃₁ (≣-to-≡₁ H))
≡₁-to-≃₁-τ-lemma {A} {B} p ⟨ a  , b  , c  ⟩ H with ≣-trans (≣-cong ≡₁-to-≣ H) (≣-uniq (≡₁-to-≣ (≃₁-to-≡₁ (≡₁-to-≃₁ p))) (≡₁-to-≣ p))
≡₁-to-≃₁-τ-lemma {A} {B} p ⟨ ._ , b  , c  ⟩ H | ≣-refl with ≣-trans (≣-cong ≡₁-to-≃₁-cong H) (≣-dext (λ C → ≣-sym (≡₁-to-≃₁-cong-∘ p C id)))
≡₁-to-≃₁-τ-lemma {A} {B} p ⟨ ._ , ._ , c  ⟩ H | ≣-refl | ≣-refl with (≣-dext (λ C → ≣-dext (λ D → ≣-uniq (c C D) (≡₁-to-≃₁-cong-∘ p C D))))
≡₁-to-≃₁-τ-lemma {A} {B} p ⟨ ._ , ._ , ._ ⟩ H | ≣-refl | ≣-refl | ≣-refl = lemma H ≣-refl where

  lemma : ∀ {r} (H : p ≣ r) (I : (≡₁-to-≃₁ p) ≣ (≡₁-to-≃₁ r)) → (≣-to-≡₁ I) ≡₁ (≡₁-cong ≡₁-to-≃₁ (≣-to-≡₁ H))
  lemma ≣-refl ≣-refl = ≡₁-refl

≡₁-to-≃₁-τ : ∀ {A B} (p : A ≡₁ B) → (≡₁-cong ≡₁-to-≃₁ (≡₁-to-≃₁-η p)) ≡₁ (≡₁-to-≃-ε (≡₁-to-≃₁ p))
≡₁-to-≃₁-τ p = ≡₁-to-≃₁-τ-lemma p _ ≣-refl

-- Lift ≃ up a level

record _≃₂_ (A B : Set₁) : Set₂ where
  constructor ⟨_,_,_,_,_⟩
  field f : A → B
  field f⁻¹ : B → A
  field η : ∀ a → f⁻¹(f a) ≡₁ a
  field ε : ∀ b → f(f⁻¹ b) ≡₁ b
  field τ : ∀ a → ≡₁-cong f(η a) ≡₁ ε(f a)
open _≃₂_ using () renaming (f to apply₂ ; f⁻¹ to apply₂⁻¹ ; η to apply₂-η ; ε to apply₂-ε ; τ to apply₂-τ )

-- Univalence

univalence₁ : ∀ {A B} → (A ≡₁ B) ≃₂ (A ≃₁ B)
univalence₁ = ⟨ ≡₁-to-≃₁ , ≃₁-to-≡₁ , ≡₁-to-≃₁-η , ≡₁-to-≃-ε , ≡₁-to-≃₁-τ ⟩


More information about the Agda mailing list