Martin Escardo. Notes originally written for the module Advanced Functional Programming of the University of Birmingham, UK.
The empty type π
It is convenient to have an empty type π
, with no
elements at all. For example, this can be used in order to define negation, among other things.
data π : Type where
And thatβs the complete definition. The list of constructors that
define the type π
is empty.
Perhaps counter-intuitively, there is one function
π β π
, namely the identity
function. So although the type π
is empty, the type
π β π
is non-empty. In fact, the non-dependent elimination
principle generalizes that.
Proposition as types interpretation
The empty type is used to interpret βfalseβ. Because there is no way
to prove the statement false
, we use the empty type to
represent false
as a type.
0 = 1
. With proofs as programs, in order to show that a
statement represented by a type A
is false, we assume a
hypothetical element x : A
, and from this we try to build a
(necessarily impossible) element of the type π
, which is
the desired contradiction. Because of this, in logic the negation of
A
is defined as A implies false
or
A implies a contradiction
. Hence in type theory we define
the negation of a type A
to be the function type
A β π
:
Β¬_ : Type β Type Β¬ A = A β π infix 1000 Β¬_
Elimination principle
π-elim : {A : π β Type} (x : π) β A x π-elim ()
The absurd
pattern ()
expresses the fact that there is no pattern
available because the type is empty. So in the same way that we define
the type by giving an empty list of constructors, we define the function
π-elim
by giving an empty list of equations. But we
indicate this explicitly with the absurd pattern.
In terms of logic, this says that in order to show that a property
A
of elements of the empty type holds for all
x : π
, we have to do nothing, because there is no element
to check, and by doing nothing we exhaust all possibilities. This is
called vacuous
truth.
It is important to notice that this is not a mere technicality. Weβll see practical examples in due course.
The non-dependent version of the eliminator says that there is a function from the empty type to any type:π-nondep-elim : {A : Type} β π β A π-nondep-elim {A} = π-elim {Ξ» _ β A}
Definition of emptiness
On the other hand, there is a functionf : A β π
if and
only if A
has no elements, that is, if A
is
also empty. This is because if x : A
, there is no element
y : π
we can choose in order to define f x
to
be y
. In fact, we make this observation into our definition
of emptiness:
is-empty : Type β Type is-empty A = A β π
So notice that this is the same definition as that of negation.
Here is another example of a type that is empty. In the introduction we defined the identity type former_β‘_
, which we will
revisit, and we have that, for example, the type 3 β‘ 4
is empty, whereas the type 3 β‘ 3
has an element
refl 3
. Here are some examples coded in Agda:
π-is-empty : is-empty π π-is-empty = π-nondep-elim open import unit-type π-is-nonempty : Β¬ is-empty π π-is-nonempty f = f βThe last function works as follows. First we unfold the definition of
Β¬ is-empty π
to get is-empty π β π
. Unfolding
again, we get the type (π β π) β π
. So, given a
hypothetical function f : π β π
, which of course cannot
exist (and this what we are trying to conclude), we need to produce an
element of π
. We do this by simply applying the mythical
f
to β : π
. We can actually incorporate this
discussion in the Agda code, if we want:
π-is-nonempty' : Β¬ is-empty π π-is-nonempty' = Ξ³ where Ξ³ : (π β π) β π Ξ³ f = f β
Agda accepts this second version because it automatically unfolds
definitions, just as we have done above, to check whether what we have
written makes sense. In this case, Agda knows that
Β¬ is-empty π
is exactly the same thing as
(π β π) β π
by definition of Β¬
and
is-empty
. More examples are given in the file negation.