introduction to De Bruijn
This commit is contained in:
parent
0baf3a784e
commit
da1e21d4d7
2 changed files with 155 additions and 25 deletions
|
@ -8,14 +8,14 @@ permalink : /DeBruijn/
|
||||||
module plta.DeBruijn where
|
module plta.DeBruijn where
|
||||||
\end{code}
|
\end{code}
|
||||||
|
|
||||||
The previous two chapters introduced lambda calculus, with
|
The previous two chapters introduced lambda calculus, with a
|
||||||
a formalisation based on named variables, and separate definitions
|
formalisation based on named variables, and terms defined separately
|
||||||
of terms and types. We began with that approach because it is
|
from types. We began with that approach because it is traditional,
|
||||||
traditional, not because it is best. This chapter presents an
|
but it is not the one we recommend. This chapter presents an
|
||||||
alternative approach, where named variables are replaced by
|
alternative approach, where named variables are replaced by De Bruijn
|
||||||
De Bruijn indices and terms are inherently typed. Our new
|
indices and terms are inherently typed. Our new presentation is more
|
||||||
presentation is more compact, using less than half the lines of
|
compact, using less than half the lines of code required previously to
|
||||||
code required previously to do cover the same ground.
|
do cover the same ground.
|
||||||
|
|
||||||
## Imports
|
## Imports
|
||||||
|
|
||||||
|
@ -34,6 +34,131 @@ open import Relation.Nullary.Negation using (contraposition)
|
||||||
open import Relation.Nullary.Product using (_×-dec_)
|
open import Relation.Nullary.Product using (_×-dec_)
|
||||||
\end{code}
|
\end{code}
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
There is a close correspondence between the structure of a term and
|
||||||
|
the structure of the derivation showing that it is well-typed. For
|
||||||
|
example, here is the term for the Church numeral two:
|
||||||
|
|
||||||
|
twoᶜ : Term
|
||||||
|
twoᶜ = ƛ "s" ⇒ ƛ "z" ⇒ ` "s" · (` "s" · ` "z")
|
||||||
|
|
||||||
|
and here is its corresponding type derivation:
|
||||||
|
|
||||||
|
⊢twoᶜ : ∀ {A} ⇒ ∅ ⊢ two ⦂ Ch A
|
||||||
|
⊢twoᶜ = ⊢ƛ (⊢ƛ (Ax ∋s · (Ax ∋s · Ax ∋z)))
|
||||||
|
where
|
||||||
|
∋s = S ("s" ≠ "z") Z
|
||||||
|
∋z = Z
|
||||||
|
|
||||||
|
(These are both taken from
|
||||||
|
Chapter [Lambda]({{ site.baseurl }}{% link out/plta/Lambda.md %})
|
||||||
|
and you can see the corresponding derivation tree written out in full
|
||||||
|
[here]({{ site.baseurl }}{% link out/plta/Lambda.md %}/#derivation).)
|
||||||
|
The two definitions are in close correspondence, where
|
||||||
|
|
||||||
|
* `` `_ `` corresponds to `Ax`
|
||||||
|
* `ƛ_⇒_` corresponds to `⊢ƛ`
|
||||||
|
* `_·_` corresponds to `_·_`
|
||||||
|
|
||||||
|
Further, if we think of `Z` as zero and `S` as successor, then the
|
||||||
|
lookup derivation for each variable corresponds to a number which
|
||||||
|
tells us how many enclosing binding terms to count to find the binding
|
||||||
|
of that variable. Here `"z"` corresponds to `Z` or zero and `"s"`
|
||||||
|
corresponds to `S Z` or one. And, indeed, "z" is bound by the inner
|
||||||
|
abstraction (count outward past zero abstractions) and "s" is bound by
|
||||||
|
the outer abstraction (count outward past one abstraction).
|
||||||
|
|
||||||
|
In this chapter, we are going to exploit this correspondence, and
|
||||||
|
introduce a new notation for terms that simultaneously resembles the
|
||||||
|
term and its type derivation. Now we will write the following.
|
||||||
|
|
||||||
|
twoᶜ : ∅ ⊢ Ch `ℕ
|
||||||
|
twoᶜ = ƛ ƛ (# 1 · (# 1 · # 0))
|
||||||
|
|
||||||
|
A variable is represented by a natural number (written with `Z` and `S`, and
|
||||||
|
abbreviated in the usual way), and tells us how many enclosing binding terms
|
||||||
|
to count to find the binding of that variable. Thus, `# 0` is bound at the
|
||||||
|
inner `ƛ`, and `# 1` at the outer `ƛ`.
|
||||||
|
|
||||||
|
Replacing variables by numbers in this way is called _De Bruijn
|
||||||
|
representation_, and the numbers themselves are called _De Bruijn
|
||||||
|
indices_, after the Dutch mathematician Nicolaas Govert (Dick) De
|
||||||
|
Bruijn (1918—2012), a pioneer in the creation of proof assistants.
|
||||||
|
One advantage of replacing named variables with De Bruijn indices
|
||||||
|
is that each term now has a unique representation, rather than being
|
||||||
|
represented by the equivalence class of terms under alpha renaming.
|
||||||
|
|
||||||
|
The other important feature of our chosen representation is that it is
|
||||||
|
_inherently typed_. In the previous two chapters, the definition of
|
||||||
|
terms and the definition of types are completely separate. All terms
|
||||||
|
have type `Term`, and nothing in Agda prevents one from writing a
|
||||||
|
nonsense term such as `` `zero · `suc `zero `` which has no type. Such
|
||||||
|
terms that exist independent of types are sometimes called _preterms_
|
||||||
|
or _raw terms_. Here we are going to replace the type `Term` of raw
|
||||||
|
terms by the more sophisticated type `Γ ⊢ A` of inherently typed
|
||||||
|
terms, which in context `Γ` have type `A`.
|
||||||
|
|
||||||
|
While these two choices fit well, they are independent. One can use
|
||||||
|
De Bruijn indices in raw terms, or (with more difficulty) have
|
||||||
|
inherently typed terms with names. In
|
||||||
|
Chapter [Untyped]({{ site.baseurl }}{% link out/plta/Untyped.md %},
|
||||||
|
we will terms that are not typed with De Bruijn indices
|
||||||
|
are inherently scoped.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
For each constructor
|
||||||
|
of terms `M`, there is a corresponding constructor of type derivations,
|
||||||
|
`Γ ⊢ M ⦂ A`:
|
||||||
|
|
||||||
|
* `` `_ `` corresponds to `Ax`
|
||||||
|
* `ƛ_⇒_` corresponds to `⊢ƛ`
|
||||||
|
* `_·_` corresponds to `_·_`
|
||||||
|
* `` `zero `` corresponds to `⊢zero`
|
||||||
|
* `` `suc_ `` corresponds to `⊢suc`
|
||||||
|
* `` `case_[zero⇒_|suc_⇒_] `` corresponds to `⊢case`
|
||||||
|
* `μ_⇒_` corresponds to `⊢μ`
|
||||||
|
|
||||||
|
Variable names `x` in terms correspond to derivations that
|
||||||
|
look up a name in the environment. There are two constructors of such
|
||||||
|
derivations `Z` and `S`, which behave similarly to constructors `here`
|
||||||
|
and `there` for looking up elements in a list. But derivations also
|
||||||
|
correspond to a natural number, take `Z` as zero and `S` as successor.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
∋s ∋s ∋z
|
||||||
|
------------------- `_ ------------------- `_ --------------- `_
|
||||||
|
Γ₂ ⊢ ` "s" ⦂ A ⇒ A Γ₂ ⊢ ` "s" ⦂ A ⇒ A Γ₂ ⊢ ` "z" ⦂ A
|
||||||
|
------------------- `_ ------------------------------------------ _·_
|
||||||
|
Γ₂ ⊢ ` "s" ⦂ A ⇒ A Γ₂ ⊢ ` "s" · ` "z" ⦂ A
|
||||||
|
-------------------------------------------------- _·_
|
||||||
|
Γ₂ ⊢ ` "s" · (` "s" · ` "z") ⦂ A
|
||||||
|
---------------------------------------------- ⊢ƛ
|
||||||
|
Γ₁ ⊢ ƛ "z" ⇒ ` "s" · (` "s" · ` "z") ⦂ A ⇒ A
|
||||||
|
------------------------------------------------------------- ⊢ƛ
|
||||||
|
∅ ⊢ ƛ "s" ⇒ ƛ "z" ⇒ ` "s" · (` "s" · ` "z") ⦂ (A ⇒ A) ⇒ A ⇒ A
|
||||||
|
|
||||||
|
where `∋s` and `∋z` abbreviate the two derivations:
|
||||||
|
|
||||||
|
---------------- Z
|
||||||
|
"s" ≢ "z" Γ₁ ∋ "s" ⦂ A ⇒ A
|
||||||
|
----------------------------- S ------------- Z
|
||||||
|
Γ₂ ∋ "s" ⦂ A ⇒ A Γ₂ ∋ "z" ⦂ A
|
||||||
|
|
||||||
|
and where `Γ₁ = ∅ , s ⦂ A ⇒ A` and `Γ₂ = ∅ , s ⦂ A ⇒ A , z ⦂ A`.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Syntax
|
## Syntax
|
||||||
|
|
||||||
|
|
|
@ -149,6 +149,10 @@ four = plus · two · two
|
||||||
The recursive definition of addition is similar to our original
|
The recursive definition of addition is similar to our original
|
||||||
definition of `_+_` for naturals, as given in
|
definition of `_+_` for naturals, as given in
|
||||||
Chapter [Naturals]({{ site.baseurl }}{% link out/plta/Naturals.md %}#plus).
|
Chapter [Naturals]({{ site.baseurl }}{% link out/plta/Naturals.md %}#plus).
|
||||||
|
Note that there are two different binding occurrences of the
|
||||||
|
variable "m", one in a lambda abstraction and one in a case term;
|
||||||
|
the occurrence of "m" in the argument of the case refers to the former
|
||||||
|
and the occurrence of "m" in the successor branch of the case refers to the latter.
|
||||||
Later we will confirm that two plus two is four, in other words that
|
Later we will confirm that two plus two is four, in other words that
|
||||||
the term
|
the term
|
||||||
|
|
||||||
|
@ -1021,41 +1025,43 @@ used with care, since such postulation could allow us to provide
|
||||||
evidence of _any_ proposition whatsoever, regardless of its truth.
|
evidence of _any_ proposition whatsoever, regardless of its truth.
|
||||||
|
|
||||||
|
|
||||||
### Example type derivations
|
### Example type derivations {#derivation}
|
||||||
|
|
||||||
Type derivations correspond to trees. In informal notation, here
|
Type derivations correspond to trees. In informal notation, here
|
||||||
is a type derivation for the Church numberal two:
|
is a type derivation for the Church numberal two:
|
||||||
|
|
||||||
∋s ∋s ∋z
|
∋s ∋z
|
||||||
------------------- `_ ------------------- `_ --------------- `_
|
-------------------- `_ --------------- `_
|
||||||
Γ₂ ⊢ ` "s" ⦂ A ⇒ A Γ₂ ⊢ ` "s" ⦂ A ⇒ A Γ₂ ⊢ ` "z" ⦂ A
|
∋s Γ₂ ⊢ ` "s" ⦂ `ℕ ⇒ `ℕ Γ₂ ⊢ ` "z" ⦂ `ℕ
|
||||||
------------------- `_ ------------------------------------------ _·_
|
-------------------- `_ ------------------------------------------ _·_
|
||||||
Γ₂ ⊢ ` "s" ⦂ A ⇒ A Γ₂ ⊢ ` "s" · ` "z" ⦂ A
|
Γ₂ ⊢ ` "s" ⦂ `ℕ ⇒ `ℕ Γ₂ ⊢ ` "s" · ` "z" ⦂ `ℕ
|
||||||
-------------------------------------------------- _·_
|
-------------------------------------------------- _·_
|
||||||
Γ₂ ⊢ ` "s" · (` "s" · ` "z") ⦂ A
|
Γ₂ ⊢ ` "s" · (` "s" · ` "z") ⦂ `ℕ
|
||||||
---------------------------------------------- ⊢ƛ
|
---------------------------------------------- ⊢ƛ
|
||||||
Γ₁ ⊢ ƛ "z" ⇒ ` "s" · (` "s" · ` "z") ⦂ A ⇒ A
|
Γ₁ ⊢ ƛ "z" ⇒ ` "s" · (` "s" · ` "z") ⦂ `ℕ ⇒ `ℕ
|
||||||
---------------------------------------------------------- ⊢ƛ
|
----------------------------------------------------------------- ⊢ƛ
|
||||||
∅ ⊢ ƛ "s" ⇒ ƛ "z" ⇒ ` "s" · (` "s" · ` "z") ⦂ A ⇒ A
|
∅ ⊢ ƛ "s" ⇒ ƛ "z" ⇒ ` "s" · (` "s" · ` "z") ⦂ (`ℕ ⇒ `ℕ) ⇒ `ℕ ⇒ `ℕ
|
||||||
|
|
||||||
where `∋s` and `∋z` abbreviate the two derivations:
|
where `∋s` and `∋z` abbreviate the two derivations:
|
||||||
|
|
||||||
---------------- Z
|
---------------- Z
|
||||||
"s" ≢ "z" Γ₁ ∋ "s" ⦂ A ⇒ A
|
"s" ≢ "z" Γ₁ ∋ "s" ⦂ `ℕ ⇒ `ℕ
|
||||||
----------------------------- S ------------- Z
|
----------------------------- S ------------- Z
|
||||||
Γ₂ ∋ "s" ⦂ A ⇒ A Γ₂ ∋ "z" ⦂ A
|
Γ₂ ∋ "s" ⦂ `ℕ ⇒ `ℕ Γ₂ ∋ "z" ⦂ `ℕ
|
||||||
|
|
||||||
and where `Γ₁ = ∅ , s ⦂ A ⇒ A` and `Γ₂ = ∅ , s ⦂ A ⇒ A , z ⦂ A`.
|
and where ``Γ₁ = ∅ , s ⦂ `ℕ ⇒ `ℕ`` and ``Γ₂ = ∅ , s ⦂ `ℕ ⇒ `ℕ , z ⦂ `ℕ``.
|
||||||
|
In fact, `plusᶜ` also has this typing derivation if we replace `∅` by an
|
||||||
|
arbitrary context `Γ`, and `` `ℕ `` by an arbitrary type `A`, but the above
|
||||||
|
will suffice for our purposes.
|
||||||
|
|
||||||
Here is the above typing derivation formalised in Agda.
|
Here is the above typing derivation formalised in Agda.
|
||||||
\begin{code}
|
\begin{code}
|
||||||
Ch : Type → Type
|
Ch : Type → Type
|
||||||
Ch A = (A ⇒ A) ⇒ A ⇒ A
|
Ch A = (A ⇒ A) ⇒ A ⇒ A
|
||||||
|
|
||||||
⊢twoᶜ : ∀ {A} → ∅ ⊢ twoᶜ ⦂ Ch A
|
⊢twoᶜ : ∅ ⊢ twoᶜ ⦂ Ch `ℕ
|
||||||
⊢twoᶜ = ⊢ƛ (⊢ƛ (Ax ∋s · (Ax ∋s · Ax ∋z)))
|
⊢twoᶜ = ⊢ƛ (⊢ƛ (Ax ∋s · (Ax ∋s · Ax ∋z)))
|
||||||
where
|
where
|
||||||
|
|
||||||
∋s = S ("s" ≠ "z") Z
|
∋s = S ("s" ≠ "z") Z
|
||||||
∋z = Z
|
∋z = Z
|
||||||
\end{code}
|
\end{code}
|
||||||
|
@ -1087,7 +1093,7 @@ branch of the case expression.
|
||||||
|
|
||||||
And here are typings for the remainder of the Church example.
|
And here are typings for the remainder of the Church example.
|
||||||
\begin{code}
|
\begin{code}
|
||||||
⊢plusᶜ : ∀ {A} → ∅ ⊢ plusᶜ ⦂ Ch A ⇒ Ch A ⇒ Ch A
|
⊢plusᶜ : ∅ ⊢ plusᶜ ⦂ Ch `ℕ ⇒ Ch `ℕ ⇒ Ch `ℕ
|
||||||
⊢plusᶜ = ⊢ƛ (⊢ƛ (⊢ƛ (⊢ƛ (Ax ∋m · Ax ∋s · (Ax ∋n · Ax ∋s · Ax ∋z)))))
|
⊢plusᶜ = ⊢ƛ (⊢ƛ (⊢ƛ (⊢ƛ (Ax ∋m · Ax ∋s · (Ax ∋n · Ax ∋s · Ax ∋z)))))
|
||||||
where
|
where
|
||||||
∋m = S ("m" ≠ "z") (S ("m" ≠ "s") (S ("m" ≠ "n") Z))
|
∋m = S ("m" ≠ "z") (S ("m" ≠ "s") (S ("m" ≠ "n") Z))
|
||||||
|
@ -1107,7 +1113,6 @@ And here are typings for the remainder of the Church example.
|
||||||
⊢2+2ᶜ = ⊢plusᶜ · ⊢twoᶜ · ⊢twoᶜ · ⊢sucᶜ · ⊢zero
|
⊢2+2ᶜ = ⊢plusᶜ · ⊢twoᶜ · ⊢twoᶜ · ⊢sucᶜ · ⊢zero
|
||||||
\end{code}
|
\end{code}
|
||||||
|
|
||||||
|
|
||||||
### Interaction with Agda
|
### Interaction with Agda
|
||||||
|
|
||||||
Construction of a type derivation may be done interactively.
|
Construction of a type derivation may be done interactively.
|
||||||
|
|
Loading…
Reference in a new issue