Consider the following extracted piece of code for proving the "Unicity of Typing" for variable in Agda:
unicity : ∀ {Γ₁ Γ₂ e τ₁ τ₂} → (Γ₁ ⊢ e ∷ τ₁) → (Γ₂ ⊢ e ∷ τ₂) → (Γ₁ ≈ Γ₂) → (τ₁ ∼ τ₂)
unicity (VarT here) (VarT here) (_ , ( τ∼ , _ )) = τ∼
unicity (VarT here) (VarT (ski`p {α = α} lk2)) (s≡s' , ( _ , _ )) = ⊥-elim (toWitnessFalse α (toWitness` s≡s'))
unicity (VarT (skip {α = α} lk1)) (VarT here) (s'≡s , ( _ , _ )) = ⊥-elim (toWitnessFalse α (toWitness s'≡s))
unicity (VarT (skip lk1)) (VarT (skip lk2)) (_ ,( _ , Γ≈ )) = unicity (VarT lk1) (VarT lk2) Γ≈
I need an explanation on the working of ⊥-elim
, toWitnessFalse
and toWitness
. Also, what do the expressions ⊤
and ⊥
mean/stand for?
⊥
is the empty type, so (in a total, consistent language) you can never construct a value of type ⊥
. But this also means that any proposition you can think of, follows from ⊥
. This is what ⊥-elim
witnesses:
⊥-elim : ∀ {w} {Whatever : Set w} → ⊥ → Whatever
This is very useful in practice because you might be writing proofs under some assumption, and some of those assumptions might be ⊥
, or they might be negative statements (A → ⊥
for some A
) and you can prove the A
as well, etc. Then, what you find out is effectively that you don't have to care about that particular branch anymore, since it is impossible; but then, just because you don't care, you still have to formally satisfy the result type somehow. This is what ⊥-elim
gives you.
toWitness
's type and related definitions are as follows:
T : Bool → Set
T true = ⊤
T false = ⊥
⌊_⌋ : ∀ {p} {P : Set p} → Dec P → Bool
⌊ yes _ ⌋ = true
⌊ no _ ⌋ = false
True : ∀ {p} {P : Set p} → Dec P → Set
True Q = T ⌊ Q ⌋
toWitness : ∀ {p} {P : Set p} {Q : Dec P} → True Q → P
Given a Q : Dec P
, True Q
is either ⊤
(if Q = yes _
) or ⊥
(if Q = no _
). The only way to call toWitness
, then, is to have Q
say that P
is true and pass the trivial unit constructor tt : ⊤
; the only other possibility would be to have Q
say that P
is false, and pass as an argument a ⊥
but as we've seen, that's not possible. In summary, toWitness
says that if Q
tells us the decision that P
holds, then we can get a proof of P
from Q
.
toWitnessFalse
is exactly the same with the roles reversed: if Q
tells us the decision that P
doesn't hold, then we can get a proof of ¬ P
from Q
.