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: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 someA
) and you can prove theA
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:Given a
Q : Dec P
,True Q
is either⊤
(ifQ = yes _
) or⊥
(ifQ = no _
). The only way to calltoWitness
, then, is to haveQ
say thatP
is true and pass the trivial unit constructortt : ⊤
; the only other possibility would be to haveQ
say thatP
is false, and pass as an argument a⊥
but as we've seen, that's not possible. In summary,toWitness
says that ifQ
tells us the decision thatP
holds, then we can get a proof ofP
fromQ
.toWitnessFalse
is exactly the same with the roles reversed: ifQ
tells us the decision thatP
doesn't hold, then we can get a proof of¬ P
fromQ
.