--- -*-mode:agda2-*- title : "DeBruijn: Intrinsically-typed de Bruijn representation" layout : page prev : /Properties/ permalink : /DeBruijn/ next : /More/ --- ``` module plfa.part2.MyDeBruijn where ``` # TLDR: A different term representation Two main changes * nameless representation of bindings * intrinsically typed (Church style) (before: extrinsically typed, Curry style) ## same program as before, but in slightly different order and entanglement * definitions: types, terms+typing, renaming, substitution, values, (typed) reduction, * proofs of properties: preservation, progress ## payoff * nameless: * no issues with name comparison in proofs * fully general definition of substitution including open terms * intrinsic: preservation trivially obtained by definition of reduction The previous two chapters introduced lambda calculus, with a formalisation based on named variables, and terms defined separately from types. We began with that approach because it is traditional, but it is not the one we recommend. This chapter presents an alternative approach, where named variables are replaced by de Bruijn indices and terms are indexed by their types. Our new presentation is more compact, using substantially fewer lines of code to cover the same ground. There are two fundamental approaches to typed lambda calculi. One approach, followed in the last two chapters, is to first define terms and then define types. Terms exist independent of types, and may have types assigned to them by separate typing rules. Another approach, followed in this chapter, is to first define types and then define terms. Terms and type rules are intertwined, and it makes no sense to talk of a term without a type. The two approaches are sometimes called _Curry style_ and _Church style_. Following Reynolds, we will refer to them as _extrinsic_ and _intrinsic_. The particular representation described here was first proposed by Thorsten Altenkirch and Bernhard Reus. The formalisation of renaming and substitution we use is due to Conor McBride. Related work has been carried out by James Chapman, James McKinna, and many others. ## Imports ``` import Relation.Binary.PropositionalEquality as Eq open Eq using (_≡_; refl) open import Data.Empty using (⊥; ⊥-elim) open import Data.Nat using (ℕ; zero; suc; _<_; _≤?_; z≤n; s≤s) open import Relation.Nullary using (¬_) open import Relation.Nullary.Decidable using (True; toWitness) open import Function using (id; _∘_) import plfa.part2.MyLambda20220718 as Ex import plfa.part2.MyProperties20220718 as ExP ``` ## 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ᶜ = ⊢ƛ (⊢ƛ (⊢` ∋s · (⊢` ∋s · ⊢` ∋z))) where ∋s = S′ Z ∋z = Z (These are both taken from Chapter [Lambda](/Lambda/) and you can see the corresponding derivation tree written out in full [here](/Lambda/#derivation).) The two definitions are in close correspondence, where: * `` `_ `` corresponds to `` ⊢` `` * `ƛ_⇒_` 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 represents 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 _intrinsically 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 type `Γ ⊢ A` of intrinsically-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 have intrinsically-typed terms with names. In Chapter [Untyped](/Untyped/), we will introduce terms with de Bruijn indices that are intrinsically scoped but not typed. ## A second example De Bruijn indices can be tricky to get the hang of, so before proceeding further let's consider a second example. Here is the term that adds two naturals: plus : Term plus = μ "+" ⇒ ƛ "m" ⇒ ƛ "n" ⇒ case ` "m" [zero⇒ ` "n" |suc "m" ⇒ `suc (` "+" · ` "m" · ` "n") ] Note variable `"m"` is bound twice, once in a lambda abstraction and once in the successor branch of the case. Any appearance of `"m"` in the successor branch must refer to the latter binding, due to shadowing. Here is its corresponding type derivation: ⊢plus : ∅ ⊢ plus ⦂ `ℕ ⇒ `ℕ ⇒ `ℕ ⊢plus = ⊢μ (⊢ƛ (⊢ƛ (⊢case (⊢` ∋m) (⊢` ∋n) (⊢suc (⊢` ∋+ · ⊢` ∋m′ · ⊢` ∋n′))))) where ∋+ = (S′ (S′ (S′ Z))) ∋m = (S′ Z) ∋n = Z ∋m′ = Z ∋n′ = (S′ Z) The two definitions are in close correspondence, where in addition to the previous correspondences we have: * `` `zero `` corresponds to `⊢zero` * `` `suc_ `` corresponds to `⊢suc` * `` case_[zero⇒_|suc_⇒_] `` corresponds to `⊢case` * `μ_⇒_` corresponds to `⊢μ` Note the two lookup judgments `∋m` and `∋m′` refer to two different bindings of variables named `"m"`. In contrast, the two judgments `∋n` and `∋n′` both refer to the same binding of `"n"` but accessed in different contexts, the first where `"n"` is the last binding in the context, and the second after `"m"` is bound in the successor branch of the case. Here is the term and its type derivation in the notation of this chapter: plus : ∀ {Γ} → Γ ⊢ `ℕ ⇒ `ℕ ⇒ `ℕ plus = μ ƛ ƛ case (# 1) (# 0) (`suc (# 3 · # 0 · # 1)) Reading from left to right, each de Bruijn index corresponds to a lookup derivation: * `# 1` corresponds to `∋m` * `# 0` corresponds to `∋n` * `# 3` corresponds to `∋+` * `# 0` corresponds to `∋m′` * `# 1` corresponds to `∋n′` The de Bruijn index counts the number of `S` constructs in the corresponding lookup derivation. Variable `"n"` bound in the inner abstraction is referred to as `# 0` in the zero branch of the case but as `# 1` in the successor branch of the case, because of the intervening binding. Variable `"m"` bound in the lambda abstraction is referred to by the first `# 1` in the code, while variable `"m"` bound in the successor branch of the case is referred to by the second `# 0`. There is no shadowing: with variable names, there is no way to refer to the former binding in the scope of the latter, but with de Bruijn indices it could be referred to as `# 2`. ## Order of presentation In the current chapter, the use of intrinsically-typed terms necessitates that we cannot introduce operations such as substitution or reduction without also showing that they preserve types. Hence, the order of presentation must change. The syntax of terms now incorporates their typing rules, and the definition of values now incorporates the Canonical Forms lemma. The definition of substitution is somewhat more involved, but incorporates the trickiest part of the previous proof, the lemma establishing that substitution preserves types. The definition of reduction incorporates preservation, which no longer requires a separate proof. ## Syntax We now begin our formal development. First, we get all our infix declarations out of the way. We list separately operators for judgments, types, and terms: ``` infix 4 _⊢_ infix 4 _∋_ infixl 5 _▷_ infixr 7 _⇒_ infix 5 ƛ_ infix 5 μ_ infixl 7 _·_ infix 8 `suc_ infix 9 `_ infix 9 S_ infix 9 #_ ``` #################### Since terms are intrinsically typed, we must define types and contexts before terms. ### Types As before, we have just two types, functions and naturals. The formal definition is unchanged: ``` data Type : Set where _⇒_ : Type → Type → Type `ℕ : Type ``` ### Contexts Contexts are as before, but we drop the names. Contexts are formalised as follows: ``` data Context : Set where ∅ : Context _▷_ : Context → Type → Context ``` A context is just a list of types, with the type of the most recently bound variable on the right. As before, we let `Γ` and `Δ` range over contexts. We write `∅` for the empty context, and `Γ ▷ A` for the context `Γ` extended by type `A`. For example ``` _ : Context _ = ∅ ▷ `ℕ ⇒ `ℕ ▷ `ℕ ``` is a context with two variables in scope, where the outer bound one has type `` `ℕ ⇒ `ℕ ``, and the inner bound one has type `` `ℕ ``. ### Variables and the lookup judgment Intrinsically-typed variables correspond to the lookup judgment. They are represented by de Bruijn indices, and hence also correspond to natural numbers. We write Γ ∋ A for variables which in context `Γ` have type `A`. The lookup judgement is formalised by a datatype indexed by a context and a type. It looks exactly like the old lookup judgment, but with all variable names dropped: ``` data _∋_ : Context → Type → Set where Z : ∀ {Γ A} --------- → Γ ▷ A ∋ A S_ : ∀ {Γ A B} → Γ ∋ A --------- → Γ ▷ B ∋ A ``` Constructor `S` no longer requires an additional parameter, since without names shadowing is no longer an issue. Now constructors `Z` and `S` correspond even more closely to the constructors `here` and `there` for the element-of relation `_∈_` on lists, as well as to constructors `zero` and `suc` for natural numbers. For example, consider the following old-style lookup judgments: * `` ∅ ▷ "s" ⦂ `ℕ ⇒ `ℕ ▷ "z" ⦂ `ℕ ∋ "z" ⦂ `ℕ `` * `` ∅ ▷ "s" ⦂ `ℕ ⇒ `ℕ ▷ "z" ⦂ `ℕ ∋ "s" ⦂ `ℕ ⇒ `ℕ `` They correspond to the following intrinsically-typed variables: ``` _ : ∅ ▷ `ℕ ⇒ `ℕ ▷ `ℕ ∋ `ℕ _ = Z _ : ∅ ▷ `ℕ ⇒ `ℕ ▷ `ℕ ∋ `ℕ ⇒ `ℕ _ = S Z ``` In the given context, `"z"` is represented by `Z` (as the most recently bound variable), and `"s"` by `S Z` (as the next most recently bound variable). ### Terms and the typing judgment Intrinsically-typed terms correspond to the typing judgment. We write Γ ⊢ A for terms which in context `Γ` have type `A`. The judgement is formalised by a datatype indexed by a context and a type. It looks exactly like the old typing judgment, but with all terms and variable names dropped: ``` data _⊢_ : Context → Type → Set where `_ : ∀ {Γ A} → Γ ∋ A ----- → Γ ⊢ A ƛ_ : ∀ {Γ A B} → Γ ▷ A ⊢ B --------- → Γ ⊢ A ⇒ B _·_ : ∀ {Γ A B} → Γ ⊢ A ⇒ B → Γ ⊢ A --------- → Γ ⊢ B `zero : ∀ {Γ} --------- → Γ ⊢ `ℕ `suc_ : ∀ {Γ} → Γ ⊢ `ℕ ------ → Γ ⊢ `ℕ case : ∀ {Γ A} → Γ ⊢ `ℕ → Γ ⊢ A → Γ ▷ `ℕ ⊢ A ---------- → Γ ⊢ A μ_ : ∀ {Γ A} → Γ ▷ A ⊢ A --------- → Γ ⊢ A -- μμ : ∀ {Γ A B } -- → (V : Γ ▷ A ⇒ B ⊢ A ⇒ B) -- → Value V -- ---------------------- -- → Γ ⊢ A ⇒ B ``` The definition exploits the close correspondence between the structure of terms and the structure of a derivation showing that it is well typed: now we use the derivation _as_ the term. For example, consider the following old-style typing judgments: * `` ∅ ▷ "s" ⦂ `ℕ ⇒ `ℕ ▷ "z" ⦂ `ℕ ⊢ ` "z" ⦂ `ℕ `` * `` ∅ ▷ "s" ⦂ `ℕ ⇒ `ℕ ▷ "z" ⦂ `ℕ ⊢ ` "s" ⦂ `ℕ ⇒ `ℕ `` * `` ∅ ▷ "s" ⦂ `ℕ ⇒ `ℕ ▷ "z" ⦂ `ℕ ⊢ ` "s" · ` "z" ⦂ `ℕ `` * `` ∅ ▷ "s" ⦂ `ℕ ⇒ `ℕ ▷ "z" ⦂ `ℕ ⊢ ` "s" · (` "s" · ` "z") ⦂ `ℕ `` * `` ∅ ▷ "s" ⦂ `ℕ ⇒ `ℕ ⊢ (ƛ "z" ⇒ ` "s" · (` "s" · ` "z")) ⦂ `ℕ ⇒ `ℕ `` * `` ∅ ⊢ ƛ "s" ⇒ ƛ "z" ⇒ ` "s" · (` "s" · ` "z")) ⦂ (`ℕ ⇒ `ℕ) ⇒ `ℕ ⇒ `ℕ `` They correspond to the following intrinsically-typed terms: ``` _ : ∅ ▷ `ℕ ⇒ `ℕ ▷ `ℕ ⊢ `ℕ _ = ` Z _ : ∅ ▷ `ℕ ⇒ `ℕ ▷ `ℕ ⊢ `ℕ ⇒ `ℕ _ = ` S Z _ : ∅ ▷ `ℕ ⇒ `ℕ ▷ `ℕ ⊢ `ℕ _ = ` S Z · ` Z _ : ∅ ▷ `ℕ ⇒ `ℕ ▷ `ℕ ⊢ `ℕ _ = ` S Z · (` S Z · ` Z) _ : ∅ ▷ `ℕ ⇒ `ℕ ⊢ `ℕ ⇒ `ℕ _ = ƛ (` S Z · (` S Z · ` Z)) _ : ∅ ⊢ (`ℕ ⇒ `ℕ) ⇒ `ℕ ⇒ `ℕ _ = ƛ ƛ (` S Z · (` S Z · ` Z)) ``` The final term represents the Church numeral two. ### Abbreviating de Bruijn indices We define a helper function that computes the length of a context, which will be useful in making sure an index is within context bounds: ``` length : Context → ℕ length ∅ = zero length (Γ ▷ _) = suc (length Γ) ``` We can use a natural number to select a type from a context: ``` lookup : {Γ : Context} → {n : ℕ} → (p : n < length Γ) → Type lookup {(_ ▷ A)} {zero} (s≤s z≤n) = A lookup {(Γ ▷ _)} {(suc n)} (s≤s p) = lookup p ``` We intend to apply the function only when the natural is shorter than the length of the context, which is witnessed by `p`. Given the above, we can convert a natural to a corresponding de Bruijn index, looking up its type in the context: ``` count : ∀ {Γ} → {n : ℕ} → (p : n < length Γ) → Γ ∋ lookup p count {_ ▷ _} {zero} (s≤s z≤n) = Z count {Γ ▷ _} {(suc n)} (s≤s p) = S (count p) ``` We can then introduce a convenient abbreviation for variables: ``` #_ : ∀ {Γ} → (n : ℕ) → {n∈Γ : True (suc n ≤? length Γ)} -------------------------------- → Γ ⊢ lookup (toWitness n∈Γ) #_ n {n∈Γ} = ` count (toWitness n∈Γ) ``` Function `#_` takes an implicit argument `n∈Γ` that provides evidence for `n` to be within the context's bounds. Recall that [`True`](/Decidable/#proof-by-reflection), [`_≤?_`](/Decidable/#the-best-of-both-worlds) and [`toWitness`](/Decidable/#decidables-from-booleans-and-booleans-from-decidables) are defined in Chapter [Decidable](/Decidable/). The type of `n∈Γ` guards against invoking `#_` on an `n` that is out of context bounds. Finally, in the return type `n∈Γ` is converted to a witness that `n` is within the bounds. With this abbreviation, we can rewrite the Church numeral two more compactly: ``` _ : ∅ ⊢ (`ℕ ⇒ `ℕ) ⇒ `ℕ ⇒ `ℕ _ = ƛ ƛ (# 1 · (# 1 · # 0)) ``` ### Test examples We repeat the test examples from Chapter [Lambda](/Lambda/). You can find them [here](/Lambda/#derivation) for comparison. First, computing two plus two on naturals: ``` two : ∀ {Γ} → Γ ⊢ `ℕ two = `suc `suc `zero plus : ∀ {Γ} → Γ ⊢ `ℕ ⇒ `ℕ ⇒ `ℕ plus = μ ƛ ƛ (case (# 1) (# 0) (`suc (# 3 · # 0 · # 1))) 2+2 : ∀ {Γ} → Γ ⊢ `ℕ 2+2 = plus · two · two ``` We generalise to arbitrary contexts because later we will give examples where `two` appears nested inside binders. Next, computing two plus two on Church numerals: ``` Ch : Type → Type Ch A = (A ⇒ A) ⇒ A ⇒ A twoᶜ : ∀ {Γ A} → Γ ⊢ Ch A twoᶜ = ƛ ƛ (# 1 · (# 1 · # 0)) plusᶜ : ∀ {Γ A} → Γ ⊢ Ch A ⇒ Ch A ⇒ Ch A plusᶜ = ƛ ƛ ƛ ƛ (# 3 · # 1 · (# 2 · # 1 · # 0)) sucᶜ : ∀ {Γ} → Γ ⊢ `ℕ ⇒ `ℕ sucᶜ = ƛ `suc (# 0) 2+2ᶜ : ∀ {Γ} → Γ ⊢ `ℕ 2+2ᶜ = plusᶜ · twoᶜ · twoᶜ · sucᶜ · `zero ``` As before we generalise everything to arbitrary contexts. While we are at it, we also generalise `twoᶜ` and `plusᶜ` to Church numerals over arbitrary types. #### Exercise `mul` (recommended) Write out the definition of a lambda term that multiplies two natural numbers, now adapted to the intrinsically-typed de Bruijn representation. ``` mul : ∀ {Γ} → Γ ⊢ `ℕ ⇒ `ℕ ⇒ `ℕ mul = μ ƛ ƛ (case (# 1) (`zero) (plus · # 1 · (# 3 · # 0 · # 1))) three : ∀ {Γ} → Γ ⊢ `ℕ three = `suc two ``` ## Renaming Renaming is a necessary prelude to substitution, enabling us to "rebase" a term from one context to another. It corresponds directly to the renaming result from the previous chapter, but here the theorem that ensures renaming preserves typing also acts as code that performs renaming. As before, we first need an extension lemma that allows us to extend the context when we encounter a binder. Given a map from variables in one context to variables in another, extension yields a map from the first context extended to the second context similarly extended. It looks exactly like the old extension lemma, but with all names and terms dropped: ``` Ren : Context → Context → Set Ren Γ Δ = ∀ {A} → Γ ∋ A → Δ ∋ A ext : ∀ {Γ Δ} → Ren Γ Δ --------------------------------- → (∀ {B} → Ren (Γ ▷ B) (Δ ▷ B)) ext ρ Z = Z ext ρ (S x) = S (ρ x) ``` Let `ρ` be the name of the map that takes variables in `Γ` to variables in `Δ`. Consider the de Bruijn index of the variable in `Γ ▷ B`: * If it is `Z`, which has type `B` in `Γ ▷ B`, then we return `Z`, which also has type `B` in `Δ ▷ B`. * If it is `S x`, for some variable `x` in `Γ`, then `ρ x` is a variable in `Δ`, and hence `S (ρ x)` is a variable in `Δ ▷ B`. With extension under our belts, it is straightforward to define renaming. If variables in one context map to variables in another, then terms in the first context map to terms in the second: ``` rename : ∀ {Γ Δ} → Ren Γ Δ ----------------------- → (∀ {A} → Γ ⊢ A → Δ ⊢ A) rename ρ (` x) = ` (ρ x) rename ρ (ƛ N) = ƛ (rename (ext ρ) N) rename ρ (L · M) = (rename ρ L) · (rename ρ M) rename ρ (`zero) = `zero rename ρ (`suc M) = `suc (rename ρ M) rename ρ (case L M N) = case (rename ρ L) (rename ρ M) (rename (ext ρ) N) rename ρ (μ N) = μ (rename (ext ρ) N) ``` Let `ρ` be the name of the map that takes variables in `Γ` to variables in `Δ`. Let's unpack the first three cases: * If the term is a variable, simply apply `ρ`. * If the term is an abstraction, use the previous result to extend the map `ρ` suitably and recursively rename the body of the abstraction. * If the term is an application, recursively rename both the function and the argument. The remaining cases are similar, recursing on each subterm, and extending the map whenever the construct introduces a bound variable. Whereas before renaming was a result that carried evidence that a term is well typed in one context to evidence that it is well typed in another context, now it actually transforms the term, suitably altering the bound variables. Type checking the code in Agda ensures that it is only passed and returns terms that are well typed by the rules of simply-typed lambda calculus. Here is an example of renaming a term with one free and one bound variable: ``` M₀ : ∅ ▷ `ℕ ⇒ `ℕ ⊢ `ℕ ⇒ `ℕ M₀ = ƛ (# 1 · (# 1 · # 0)) M₁ : ∅ ▷ `ℕ ⇒ `ℕ ▷ `ℕ ⊢ `ℕ ⇒ `ℕ M₁ = ƛ (# 2 · (# 2 · # 0)) _ : rename S_ M₀ ≡ M₁ _ = refl ``` In general, `rename S_` will increment the de Bruijn index for each free variable by one, while leaving the index for each bound variable unchanged. The code achieves this naturally: the map originally increments each variable by one, and is extended for each bound variable by a map that leaves it unchanged. We will see below that renaming by `S_` plays a key role in substitution. For traditional uses of de Bruijn indices without intrinsic typing, this is a little tricky. The code keeps count of a number where all greater indexes are free and all smaller indexes bound, and increment only indexes greater than the number. It's easy to have off-by-one errors. But it's hard to imagine an off-by-one error that preserves typing, and hence the Agda code for intrinsically-typed de Bruijn terms is intrinsically reliable. #################### Interlude Why is this a good definition? We can verify properties of renaming! We can show the action of renaming is _functorial_, i.e. * renaming with the identity function is identity on terms * the composition of renamings is a renaming ``` ext-id-aux : ∀ {Γ A A′} {x : Γ ▷ A ∋ A′} → ∀ (ρ : Ren Γ Γ) → (∀ {B} {x : Γ ∋ B} → ρ x ≡ id x) → ext ρ x ≡ x ext-id-aux {x = Z} ρ ρx≡x = refl ext-id-aux {x = S x} ρ ρx≡x rewrite ρx≡x {x = x} = refl ext-id : ∀ {Γ A B} {x : Γ ▷ A ∋ B} → ext id x ≡ x ext-id {x = Z} = refl ext-id {x = S x} = refl ext-comp : ∀ {Γ Δ Θ A B} {x : Γ ▷ A ∋ B} {ρ : Ren Γ Δ} {ρ′ : Ren Δ Θ} → ext (ρ′ ∘ ρ) x ≡ ext ρ′ (ext ρ x) ext-comp {x = Z} = refl ext-comp {x = S x} = refl ren-id-aux : ∀ {Γ A} {M : Γ ⊢ A} → ∀ (ρ : Ren Γ Γ) → (∀ {B} {x : Γ ∋ B} → ρ x ≡ id x) → rename ρ M ≡ M ren-id-aux {M = ` x} ρ ρ≡id rewrite ρ≡id {x = x} = refl ren-id-aux {M = ƛ M} ρ ρ≡id rewrite ren-id-aux {M = M} (ext ρ) (ext-id-aux ρ ρ≡id) = refl ren-id-aux {M = M · N} ρ ρ≡id rewrite ren-id-aux {M = M} ρ ρ≡id | ren-id-aux {M = N} ρ ρ≡id = refl ren-id-aux {M = `zero} ρ ρ≡id = refl ren-id-aux {M = `suc M} ρ ρ≡id rewrite ren-id-aux {M = M} ρ ρ≡id = refl ren-id-aux {M = case M M₁ M₂} ρ ρ≡id rewrite ren-id-aux {M = M} ρ ρ≡id | ren-id-aux {M = M₁} ρ ρ≡id | ren-id-aux {M = M₂} (ext ρ) (ext-id-aux ρ ρ≡id) = refl ren-id-aux {M = μ M} ρ ρ≡id rewrite ren-id-aux {M = M} (ext ρ) (ext-id-aux ρ ρ≡id) = refl ren-id : ∀ {Γ A} {M : Γ ⊢ A} → rename id M ≡ M ren-id = ren-id-aux id refl lemma-ren-ext : ∀ {Γ Δ Θ} {ρ′ : Ren Γ Δ} {ρ″ : Ren Δ Θ} → (ρ : Ren Γ Θ) → (∀ {B} {x : Γ ∋ B} → ρ x ≡ ρ″ (ρ′ x)) → (∀ {B C} {x : Γ ▷ C ∋ B} → (ext ρ) x ≡ (ext ρ″) ((ext ρ′) x)) lemma-ren-ext ρ ρ≡ρ″∘ρ′ {x = Z} = refl lemma-ren-ext ρ ρ≡ρ″∘ρ′ {x = S x} rewrite ρ≡ρ″∘ρ′ {x = x} = refl ren-comp-aux : ∀ {Γ Δ Θ A} {M : Γ ⊢ A} {ρ′ : Ren Γ Δ} {ρ″ : Ren Δ Θ} → (ρ : Ren Γ Θ) → (∀ {B} {x : Γ ∋ B} → ρ x ≡ ρ″ (ρ′ x)) → rename ρ M ≡ rename ρ″ (rename ρ′ M) ren-comp-aux {M = ` x} ρ ρ≡ρ″∘ρ′ rewrite ρ≡ρ″∘ρ′ {x = x} = refl ren-comp-aux {M = ƛ M}{ρ′}{ρ″} ρ ρ≡ρ″∘ρ′ rewrite ren-comp-aux {M = M} {ρ′ = ext ρ′} {ρ″ = ext ρ″} (ext ρ) (lemma-ren-ext ρ ρ≡ρ″∘ρ′) = refl ren-comp-aux {M = M · N}{ρ′}{ρ″} ρ ρ≡ρ″∘ρ′ rewrite ren-comp-aux {M = M}{ρ′}{ρ″} ρ ρ≡ρ″∘ρ′ | ren-comp-aux {M = N}{ρ′}{ρ″} ρ ρ≡ρ″∘ρ′ = refl ren-comp-aux {M = `zero} ρ ρ≡ρ″∘ρ′ = refl ren-comp-aux {M = `suc M}{ρ′}{ρ″} ρ ρ≡ρ″∘ρ′ rewrite ren-comp-aux {M = M}{ρ′}{ρ″} ρ ρ≡ρ″∘ρ′ = refl ren-comp-aux {M = case M M₁ M₂}{ρ′}{ρ″} ρ ρ≡ρ″∘ρ′ rewrite ren-comp-aux {M = M}{ρ′}{ρ″} ρ ρ≡ρ″∘ρ′ | ren-comp-aux {M = M₁}{ρ′}{ρ″} ρ ρ≡ρ″∘ρ′ | ren-comp-aux {M = M₂}{ρ′ = ext ρ′} {ρ″ = ext ρ″} (ext ρ) (lemma-ren-ext ρ ρ≡ρ″∘ρ′) = refl ren-comp-aux {M = μ M}{ρ′}{ρ″} ρ ρ≡ρ″∘ρ′ rewrite ren-comp-aux {M = M}{ρ′ = ext ρ′} {ρ″ = ext ρ″} (ext ρ) (lemma-ren-ext ρ ρ≡ρ″∘ρ′) = refl ren-comp : ∀ {Γ Δ Θ A} {M : Γ ⊢ A} {ρ′ : Ren Γ Δ} {ρ″ : Ren Δ Θ} → rename (ρ″ ∘ ρ′) M ≡ rename ρ″ (rename ρ′ M) ren-comp {ρ′ = ρ′}{ρ″} = ren-comp-aux (ρ″ ∘ ρ′) refl ``` #################### ## Simultaneous Substitution Because de Bruijn indices free us of concerns with renaming, it becomes easy to provide a definition of substitution that is more general than the one considered previously. Instead of substituting a closed term for a single variable, it provides a map that takes each free variable of the original term to another term. Further, the substituted terms are over an arbitrary context, and need not be closed. The structure of the definition and the proof is remarkably close to that for renaming. Again, we first need an extension lemma that allows us to extend the context when we encounter a binder. Whereas renaming concerned a map from variables in one context to variables in another, substitution takes a map from variables in one context to _terms_ in another. Given a map from variables in one context to terms over another, extension yields a map from the first context extended to the second context similarly extended: ``` Sub : Context → Context → Set Sub Γ Δ = ∀ {A} → Γ ∋ A → Δ ⊢ A exts : ∀ {Γ Δ} → Sub Γ Δ ----------------------------- → (∀ {B} → Sub (Γ ▷ B) (Δ ▷ B)) exts σ Z = ` Z exts σ (S x) = rename S_ (σ x) ``` Let `σ` be the name of the map that takes variables in `Γ` to terms over `Δ`. Consider the de Bruijn index of the variable in `Γ ▷ B`: * If it is `Z`, which has type `B` in `Γ ▷ B`, then we return the term `` ` Z``, which also has type `B` in `Δ ▷ B`. * If it is `S x`, for some variable `x` in `Γ`, then `σ x` is a term in `Δ`, and hence `rename S_ (σ x)` is a term in `Δ ▷ B`. This is why we had to define renaming first, since we require it to convert a term over context `Δ` to a term over the extended context `Δ ▷ B`. With extension under our belts, it is straightforward to define substitution. If variables in one context map to terms over another, then terms in the first context map to terms in the second: ``` subst : ∀ {Γ Δ} → Sub Γ Δ ----------------------- → (∀ {A} → Γ ⊢ A → Δ ⊢ A) subst σ (` k) = σ k subst σ (ƛ N) = ƛ (subst (exts σ) N) subst σ (L · M) = (subst σ L) · (subst σ M) subst σ (`zero) = `zero subst σ (`suc M) = `suc (subst σ M) subst σ (case L M N) = case (subst σ L) (subst σ M) (subst (exts σ) N) subst σ (μ N) = μ (subst (exts σ) N) ``` Let `σ` be the name of the map that takes variables in `Γ` to terms over `Δ`. Let's unpack the first three cases: * If the term is a variable, simply apply `σ`. * If the term is an abstraction, use the previous result to extend the map `σ` suitably and recursively substitute over the body of the abstraction. * If the term is an application, recursively substitute over both the function and the argument. The remaining cases are similar, recursing on each subterm, and extending the map whenever the construct introduces a bound variable. ## Single substitution From the general case of substitution for multiple free variables it is easy to define the special case of substitution for one free variable: ``` _[_] : ∀ {Γ A B} → Γ ▷ B ⊢ A → Γ ⊢ B --------- → Γ ⊢ A _[_] {Γ} {A} {B} N M = subst {Γ ▷ B} {Γ} σ {A} N where σ : ∀ {A} → Γ ▷ B ∋ A → Γ ⊢ A σ Z = M σ (S x) = ` x ``` In a term of type `A` over context `Γ ▷ B`, we replace the variable of type `B` by a term of type `B` over context `Γ`. To do so, we use a map from the context `Γ ▷ B` to the context `Γ`, that maps the last variable in the context to the term of type `B` and every other free variable to itself. Let's try to construct an example for substitution where substituend contains free variables. This is outside the scope of the previous definition (ƛ "y" ⇒ ` "x") [ "x" := ` "y" ] ≢ (ƛ "y" ⇒ "y") ≡ (ƛ "y'" ⇒ "y") ``` Γ₀ : Context -- bindings for "y" and "x" Γ₀ = ∅ ▷ `ℕ ▷ `ℕ --------- y x My : ∅ ▷ `ℕ ⊢ `ℕ My = ` Z My⇒x : Γ₀ ⊢ `ℕ ⇒ `ℕ My⇒x = ƛ (# 1) My⇒x/x:=y : ∅ ▷ `ℕ ⊢ `ℕ ⇒ `ℕ My⇒x/x:=y = My⇒x [ My ] Mafter≡ : My⇒x/x:=y ≡ ƛ (# 1) Mafter≡ = refl ``` Consider the previous example: * `` (ƛ "z" ⇒ ` "s" · (` "s" · ` "z")) [ "s" := sucᶜ ] `` yields `` ƛ "z" ⇒ sucᶜ · (sucᶜ · ` "z") `` Here is the example formalised: ``` M₂ : ∅ ▷ `ℕ ⇒ `ℕ ⊢ `ℕ ⇒ `ℕ M₂ = ƛ # 1 · (# 1 · # 0) M₃ : ∅ ⊢ `ℕ ⇒ `ℕ M₃ = ƛ `suc # 0 M₄ : ∅ ⊢ `ℕ ⇒ `ℕ M₄ = ƛ (ƛ `suc # 0) · ((ƛ `suc # 0) · # 0) _ : M₂ [ M₃ ] ≡ M₄ _ = refl ``` Previously, we presented an example of substitution that we did not implement, since it needed to rename the bound variable to avoid capture: * `` (ƛ "x" ⇒ ` "x" · ` "y") [ "y" := ` "x" · `zero ] `` should yield `` ƛ "z" ⇒ ` "z" · (` "x" · `zero) `` Say the bound `"x"` has type `` `ℕ ⇒ `ℕ ``, the substituted `"y"` has type `` `ℕ ``, and the free `"x"` also has type `` `ℕ ⇒ `ℕ ``. Here is the example formalised: ``` M₅ : ∅ ▷ `ℕ ⇒ `ℕ ▷ `ℕ ⊢ (`ℕ ⇒ `ℕ) ⇒ `ℕ M₅ = ƛ # 0 · # 1 M₆ : ∅ ▷ `ℕ ⇒ `ℕ ⊢ `ℕ M₆ = # 0 · `zero M₇ : ∅ ▷ `ℕ ⇒ `ℕ ⊢ (`ℕ ⇒ `ℕ) ⇒ `ℕ M₇ = ƛ (# 0 · (# 1 · `zero)) _ : M₅ [ M₆ ] ≡ M₇ _ = refl ``` The logician Haskell Curry observed that getting the definition of substitution right can be a tricky business. It can be even trickier when using de Bruijn indices, which can often be hard to decipher. Under the current approach, any definition of substitution must, of necessity, preserve types. While this makes the definition more involved, it means that once it is done the hardest work is out of the way. And combining definition with proof makes it harder for errors to sneak in. #################### Interlude Why is this a good definition of substitution? Again, there are properties that a substitution must have. In particular, substitution is a relative monad. ``` exts-id : ∀ {Γ A B} {x : Γ ▷ A ∋ B} → exts `_ x ≡ ` x exts-id = {!!} exts-comp : ∀ {Γ Δ Θ A B} {x : Γ ▷ A ∋ B} {σ : Sub Γ Δ}{σ′ : Sub Δ Θ} → subst (exts σ′) (exts σ x) ≡ exts (subst σ′ ∘ σ) x exts-comp = {!!} subst-id : ∀ {Γ A} {M : Γ ⊢ A} → subst `_ M ≡ M subst-id = {!!} -- `_ is return of the monad subst-var : ∀ {Γ Δ A} {x : Γ ∋ A} {σ : Sub Γ Δ} → subst σ (` x) ≡ σ x subst-var = {!!} subst-comp : ∀ {Γ Δ Θ A} {M : Γ ⊢ A} {σ : Sub Γ Δ}{σ′ : Sub Δ Θ} → subst σ′ (subst σ M) ≡ subst (subst σ′ ∘ σ) M subst-comp = {!!} ``` ## Values The definition of value is much as before, save that the added types incorporate the same information found in the Canonical Forms lemma: ``` data Value : ∀ {Γ A} → Γ ⊢ A → Set where V-ƛ : ∀ {Γ A B} {N : Γ ▷ A ⊢ B} --------------------------- → Value (ƛ N) V-zero : ∀ {Γ} ----------------- → Value (`zero {Γ}) V-suc : ∀ {Γ} {V : Γ ⊢ `ℕ} → Value V -------------- → Value (`suc V) ``` Here `zero` requires an implicit parameter to aid inference, much in the same way that `[]` did in [Lists](/Lists/). ## Reduction The reduction rules are the same as those given earlier, save that for each term we must specify its types. As before, we have compatibility rules that reduce a part of a term, labelled with `ξ`, and rules that simplify a constructor combined with a destructor, labelled with `β`: ``` infix 2 _—→_ data _—→_ : ∀ {Γ A} → (Γ ⊢ A) → (Γ ⊢ A) → Set where ξ-·₁ : ∀ {Γ A B} {L L′ : Γ ⊢ A ⇒ B} {M : Γ ⊢ A} → L —→ L′ --------------- → L · M —→ L′ · M ξ-·₂ : ∀ {Γ A B} {V : Γ ⊢ A ⇒ B} {M M′ : Γ ⊢ A} → Value V → M —→ M′ --------------- → V · M —→ V · M′ β-ƛ : ∀ {Γ A B} {N : Γ ▷ A ⊢ B} {W : Γ ⊢ A} → Value W -------------------- → (ƛ N) · W —→ N [ W ] ξ-suc : ∀ {Γ} {M M′ : Γ ⊢ `ℕ} → M —→ M′ ----------------- → `suc M —→ `suc M′ ξ-case : ∀ {Γ A} {L L′ : Γ ⊢ `ℕ} {M : Γ ⊢ A} {N : Γ ▷ `ℕ ⊢ A} → L —→ L′ ------------------------- → case L M N —→ case L′ M N β-zero : ∀ {Γ A} {M : Γ ⊢ A} {N : Γ ▷ `ℕ ⊢ A} ------------------- → case `zero M N —→ M β-suc : ∀ {Γ A} {V : Γ ⊢ `ℕ} {M : Γ ⊢ A} {N : Γ ▷ `ℕ ⊢ A} → Value V ---------------------------- → case (`suc V) M N —→ N [ V ] β-μ : ∀ {Γ A} {N : Γ ▷ A ⊢ A} ---------------- → μ N —→ N [ μ N ] ``` The definition states that `M —→ N` can only hold of terms `M` and `N` which _both_ have type `Γ ⊢ A` for some context `Γ` and type `A`. In other words, it is _built-in_ to our definition that reduction preserves types. There is no separate Preservation theorem to prove. The Agda type-checker validates that each term preserves types. In the case of `β` rules, preservation depends on the fact that substitution preserves types, which is built-in to our definition of substitution. Actually, the typed definition of reduction provides _more_ than preservation! The definition implies that typing is preserved even when reducing open terms (with non-empty Γ). This property is called _Subject Reduction_ (SR). ## Reflexive and transitive closure The reflexive and transitive closure is exactly as before. We simply cut-and-paste the previous definition: ``` infix 2 _—↠_ infix 1 begin_ infixr 2 _—→⟨_⟩_ infix 3 _∎ data _—↠_ {Γ A} : (Γ ⊢ A) → (Γ ⊢ A) → Set where _∎ : (M : Γ ⊢ A) ------ → M —↠ M _—→⟨_⟩_ : (L : Γ ⊢ A) {M N : Γ ⊢ A} → L —→ M → M —↠ N ------ → L —↠ N begin_ : ∀ {Γ A} {M N : Γ ⊢ A} → M —↠ N ------ → M —↠ N begin M—↠N = M—↠N ``` ## Examples We reiterate each of our previous examples. First, the Church numeral two applied to the successor function and zero yields the natural number two: ``` _ : twoᶜ · sucᶜ · `zero {∅} —↠ `suc `suc `zero _ = begin twoᶜ · sucᶜ · `zero —→⟨ ξ-·₁ (β-ƛ V-ƛ) ⟩ (ƛ (sucᶜ · (sucᶜ · # 0))) · `zero —→⟨ β-ƛ V-zero ⟩ sucᶜ · (sucᶜ · `zero) —→⟨ ξ-·₂ V-ƛ (β-ƛ V-zero) ⟩ sucᶜ · `suc `zero —→⟨ β-ƛ (V-suc V-zero) ⟩ `suc (`suc `zero) ∎ ``` As before, we need to supply an explicit context to `` `zero ``. Next, a sample reduction demonstrating that two plus two is four: ``` _ : plus {∅} · two · two —↠ `suc `suc `suc `suc `zero _ = plus · two · two —→⟨ ξ-·₁ (ξ-·₁ β-μ) ⟩ (ƛ ƛ case (` S Z) (` Z) (`suc (plus · ` Z · ` S Z))) · two · two —→⟨ ξ-·₁ (β-ƛ (V-suc (V-suc V-zero))) ⟩ (ƛ case two (` Z) (`suc (plus · ` Z · ` S Z))) · two —→⟨ β-ƛ (V-suc (V-suc V-zero)) ⟩ case two two (`suc (plus · ` Z · two)) —→⟨ β-suc (V-suc V-zero) ⟩ `suc (plus · `suc `zero · two) —→⟨ ξ-suc (ξ-·₁ (ξ-·₁ β-μ)) ⟩ `suc ((ƛ ƛ case (` S Z) (` Z) (`suc (plus · ` Z · ` S Z))) · `suc `zero · two) —→⟨ ξ-suc (ξ-·₁ (β-ƛ (V-suc V-zero))) ⟩ `suc ((ƛ case (`suc `zero) (` Z) (`suc (plus · ` Z · ` S Z))) · two) —→⟨ ξ-suc (β-ƛ (V-suc (V-suc V-zero))) ⟩ `suc (case (`suc `zero) (two) (`suc (plus · ` Z · two))) —→⟨ ξ-suc (β-suc V-zero) ⟩ `suc (`suc (plus · `zero · two)) —→⟨ ξ-suc (ξ-suc (ξ-·₁ (ξ-·₁ β-μ))) ⟩ `suc (`suc ((ƛ ƛ case (` S Z) (` Z) (`suc (plus · ` Z · ` S Z))) · `zero · two)) —→⟨ ξ-suc (ξ-suc (ξ-·₁ (β-ƛ V-zero))) ⟩ `suc (`suc ((ƛ case `zero (` Z) (`suc (plus · ` Z · ` S Z))) · two)) —→⟨ ξ-suc (ξ-suc (β-ƛ (V-suc (V-suc V-zero)))) ⟩ `suc (`suc (case `zero (two) (`suc (plus · ` Z · two)))) —→⟨ ξ-suc (ξ-suc β-zero) ⟩ `suc (`suc (`suc (`suc `zero))) ∎ ``` And finally, a similar sample reduction for Church numerals: ``` _ : plusᶜ · twoᶜ · twoᶜ · sucᶜ · `zero —↠ `suc `suc `suc `suc `zero {∅} _ = begin plusᶜ · twoᶜ · twoᶜ · sucᶜ · `zero —→⟨ ξ-·₁ (ξ-·₁ (ξ-·₁ (β-ƛ V-ƛ))) ⟩ (ƛ ƛ ƛ twoᶜ · ` S Z · (` S S Z · ` S Z · ` Z)) · twoᶜ · sucᶜ · `zero —→⟨ ξ-·₁ (ξ-·₁ (β-ƛ V-ƛ)) ⟩ (ƛ ƛ twoᶜ · ` S Z · (twoᶜ · ` S Z · ` Z)) · sucᶜ · `zero —→⟨ ξ-·₁ (β-ƛ V-ƛ) ⟩ (ƛ twoᶜ · sucᶜ · (twoᶜ · sucᶜ · ` Z)) · `zero —→⟨ β-ƛ V-zero ⟩ twoᶜ · sucᶜ · (twoᶜ · sucᶜ · `zero) —→⟨ ξ-·₁ (β-ƛ V-ƛ) ⟩ (ƛ sucᶜ · (sucᶜ · ` Z)) · (twoᶜ · sucᶜ · `zero) —→⟨ ξ-·₂ V-ƛ (ξ-·₁ (β-ƛ V-ƛ)) ⟩ (ƛ sucᶜ · (sucᶜ · ` Z)) · ((ƛ sucᶜ · (sucᶜ · ` Z)) · `zero) —→⟨ ξ-·₂ V-ƛ (β-ƛ V-zero) ⟩ (ƛ sucᶜ · (sucᶜ · ` Z)) · (sucᶜ · (sucᶜ · `zero)) —→⟨ ξ-·₂ V-ƛ (ξ-·₂ V-ƛ (β-ƛ V-zero)) ⟩ (ƛ sucᶜ · (sucᶜ · ` Z)) · (sucᶜ · `suc `zero) —→⟨ ξ-·₂ V-ƛ (β-ƛ (V-suc V-zero)) ⟩ (ƛ sucᶜ · (sucᶜ · ` Z)) · `suc (`suc `zero) —→⟨ β-ƛ (V-suc (V-suc V-zero)) ⟩ sucᶜ · (sucᶜ · `suc (`suc `zero)) —→⟨ ξ-·₂ V-ƛ (β-ƛ (V-suc (V-suc V-zero))) ⟩ sucᶜ · `suc (`suc (`suc `zero)) —→⟨ β-ƛ (V-suc (V-suc (V-suc V-zero))) ⟩ `suc (`suc (`suc (`suc `zero))) ∎ ``` ## Values do not reduce We have now completed all the definitions, which of necessity subsumed some of the propositions from the earlier development: Canonical Forms, Substitution preserves types, and Preservation. We now turn to proving the remaining results from the previous development. #### Exercise `V¬—→` (practice) Following the previous development, show values do not reduce, and its corollary, terms that reduce are not values. ``` V¬—→ : ∀ {Γ} {A} {M : Γ ⊢ A} → Value M ------------------ → ∀ {N} → ¬ (M —→ N) V¬—→ V-zero () V¬—→ (V-suc vM) (ξ-suc M—→N) = V¬—→ vM M—→N V¬—→ V-ƛ () ``` ## Progress As before, every term that is well typed and closed is either a value or takes a reduction step. The formulation of progress is just as before, but annotated with types: ``` data Progress {A} (M : ∅ ⊢ A) : Set where step : ∀ {N : ∅ ⊢ A} → M —→ N ---------- → Progress M done : Value M ---------- → Progress M ``` Here the restriction to the empty context ∅ is crucial. Otherwise, there would be further outcomes of the form "free variable applied to arguments" or "case of a free variable" or "suc of a free variable" (or just a free variable on its own). The statement and proof of progress is much as before, appropriately annotated. We no longer need to explicitly refer to the Canonical Forms lemma, since it is built-in to the definition of value: (Canonical forms not needed in Properties.progress!) ``` progress : ∀ {A} → (M : ∅ ⊢ A) → Progress M progress (` ()) progress (ƛ N) = done V-ƛ progress (L · M) with progress L ... | step L—→L′ = step (ξ-·₁ L—→L′) ... | done V-ƛ with progress M ... | step M—→M′ = step (ξ-·₂ V-ƛ M—→M′) ... | done VM = step (β-ƛ VM) progress (`zero) = done V-zero progress (`suc M) with progress M ... | step M—→M′ = step (ξ-suc M—→M′) ... | done VM = done (V-suc VM) progress (case L M N) with progress L ... | step L—→L′ = step (ξ-case L—→L′) ... | done V-zero = step (β-zero) ... | done (V-suc VL) = step (β-suc VL) progress (μ N) = step (β-μ) ``` ## Evaluation Before, we combined progress and preservation to evaluate a term. We can do much the same here, but we no longer need to explicitly refer to preservation, since it is built-in to the definition of reduction. As previously, gas is specified by a natural number: ``` record Gas : Set where constructor gas field amount : ℕ ``` When our evaluator returns a term `N`, it will either give evidence that `N` is a value or indicate that it ran out of gas: ``` data Finished {Γ A} (N : Γ ⊢ A) : Set where done : Value N ---------- → Finished N out-of-gas : ---------- Finished N ``` Given a term `L` of type `A`, the evaluator will, for some `N`, return a reduction sequence from `L` to `N` and an indication of whether reduction finished: ``` data Steps {A} : ∅ ⊢ A → Set where steps : {L N : ∅ ⊢ A} → L —↠ N → Finished N ---------- → Steps L ``` The evaluator takes gas and a term and returns the corresponding steps: ``` eval : ∀ {A} → Gas → (L : ∅ ⊢ A) ----------- → Steps L eval (gas zero) L = steps (L ∎) out-of-gas eval (gas (suc m)) L with progress L ... | done VL = steps (L ∎) (done VL) ... | step {M} L—→M with eval (gas m) M ... | steps M—↠N fin = steps (L —→⟨ L—→M ⟩ M—↠N) fin ``` The definition is a little simpler than previously, as we no longer need to invoke preservation. ## Examples We reiterate each of our previous examples. We re-define the term `sucμ` that loops forever: ``` sucμ : ∅ ⊢ `ℕ sucμ = μ (`suc (# 0)) ``` To compute the first three steps of the infinite reduction sequence, we evaluate with three steps worth of gas: ``` _ : eval (gas 3) sucμ ≡ steps (μ `suc ` Z —→⟨ β-μ ⟩ `suc (μ `suc ` Z) —→⟨ ξ-suc β-μ ⟩ `suc (`suc (μ `suc ` Z)) —→⟨ ξ-suc (ξ-suc β-μ) ⟩ `suc (`suc (`suc (μ `suc ` Z))) ∎) out-of-gas _ = refl ``` The Church numeral two applied to successor and zero: ``` _ : eval (gas 100) (twoᶜ · sucᶜ · `zero) ≡ steps ((ƛ (ƛ ` (S Z) · (` (S Z) · ` Z))) · (ƛ `suc ` Z) · `zero —→⟨ ξ-·₁ (β-ƛ V-ƛ) ⟩ (ƛ (ƛ `suc ` Z) · ((ƛ `suc ` Z) · ` Z)) · `zero —→⟨ β-ƛ V-zero ⟩ (ƛ `suc ` Z) · ((ƛ `suc ` Z) · `zero) —→⟨ ξ-·₂ V-ƛ (β-ƛ V-zero) ⟩ (ƛ `suc ` Z) · `suc `zero —→⟨ β-ƛ (V-suc V-zero) ⟩ `suc (`suc `zero) ∎) (done (V-suc (V-suc V-zero))) _ = refl ``` Two plus two is four: ``` _ : eval (gas 100) (plus · two · two) ≡ steps ((μ (ƛ (ƛ case (` (S Z)) (` Z) (`suc (` (S (S (S Z))) · ` Z · ` (S Z)))))) · `suc (`suc `zero) · `suc (`suc `zero) —→⟨ ξ-·₁ (ξ-·₁ β-μ) ⟩ (ƛ (ƛ case (` (S Z)) (` Z) (`suc ((μ (ƛ (ƛ case (` (S Z)) (` Z) (`suc (` (S (S (S Z))) · ` Z · ` (S Z)))))) · ` Z · ` (S Z))))) · `suc (`suc `zero) · `suc (`suc `zero) —→⟨ ξ-·₁ (β-ƛ (V-suc (V-suc V-zero))) ⟩ (ƛ case (`suc (`suc `zero)) (` Z) (`suc ((μ (ƛ (ƛ case (` (S Z)) (` Z) (`suc (` (S (S (S Z))) · ` Z · ` (S Z)))))) · ` Z · ` (S Z)))) · `suc (`suc `zero) —→⟨ β-ƛ (V-suc (V-suc V-zero)) ⟩ case (`suc (`suc `zero)) (`suc (`suc `zero)) (`suc ((μ (ƛ (ƛ case (` (S Z)) (` Z) (`suc (` (S (S (S Z))) · ` Z · ` (S Z)))))) · ` Z · `suc (`suc `zero))) —→⟨ β-suc (V-suc V-zero) ⟩ `suc ((μ (ƛ (ƛ case (` (S Z)) (` Z) (`suc (` (S (S (S Z))) · ` Z · ` (S Z)))))) · `suc `zero · `suc (`suc `zero)) —→⟨ ξ-suc (ξ-·₁ (ξ-·₁ β-μ)) ⟩ `suc ((ƛ (ƛ case (` (S Z)) (` Z) (`suc ((μ (ƛ (ƛ case (` (S Z)) (` Z) (`suc (` (S (S (S Z))) · ` Z · ` (S Z)))))) · ` Z · ` (S Z))))) · `suc `zero · `suc (`suc `zero)) —→⟨ ξ-suc (ξ-·₁ (β-ƛ (V-suc V-zero))) ⟩ `suc ((ƛ case (`suc `zero) (` Z) (`suc ((μ (ƛ (ƛ case (` (S Z)) (` Z) (`suc (` (S (S (S Z))) · ` Z · ` (S Z)))))) · ` Z · ` (S Z)))) · `suc (`suc `zero)) —→⟨ ξ-suc (β-ƛ (V-suc (V-suc V-zero))) ⟩ `suc case (`suc `zero) (`suc (`suc `zero)) (`suc ((μ (ƛ (ƛ case (` (S Z)) (` Z) (`suc (` (S (S (S Z))) · ` Z · ` (S Z)))))) · ` Z · `suc (`suc `zero))) —→⟨ ξ-suc (β-suc V-zero) ⟩ `suc (`suc ((μ (ƛ (ƛ case (` (S Z)) (` Z) (`suc (` (S (S (S Z))) · ` Z · ` (S Z)))))) · `zero · `suc (`suc `zero))) —→⟨ ξ-suc (ξ-suc (ξ-·₁ (ξ-·₁ β-μ))) ⟩ `suc (`suc ((ƛ (ƛ case (` (S Z)) (` Z) (`suc ((μ (ƛ (ƛ case (` (S Z)) (` Z) (`suc (` (S (S (S Z))) · ` Z · ` (S Z)))))) · ` Z · ` (S Z))))) · `zero · `suc (`suc `zero))) —→⟨ ξ-suc (ξ-suc (ξ-·₁ (β-ƛ V-zero))) ⟩ `suc (`suc ((ƛ case `zero (` Z) (`suc ((μ (ƛ (ƛ case (` (S Z)) (` Z) (`suc (` (S (S (S Z))) · ` Z · ` (S Z)))))) · ` Z · ` (S Z)))) · `suc (`suc `zero))) —→⟨ ξ-suc (ξ-suc (β-ƛ (V-suc (V-suc V-zero)))) ⟩ `suc (`suc case `zero (`suc (`suc `zero)) (`suc ((μ (ƛ (ƛ case (` (S Z)) (` Z) (`suc (` (S (S (S Z))) · ` Z · ` (S Z)))))) · ` Z · `suc (`suc `zero)))) —→⟨ ξ-suc (ξ-suc β-zero) ⟩ `suc (`suc (`suc (`suc `zero))) ∎) (done (V-suc (V-suc (V-suc (V-suc V-zero))))) _ = refl ``` And the corresponding term for Church numerals: ``` _ : eval (gas 100) (plusᶜ · twoᶜ · twoᶜ · sucᶜ · `zero) ≡ steps ((ƛ (ƛ (ƛ (ƛ ` (S (S (S Z))) · ` (S Z) · (` (S (S Z)) · ` (S Z) · ` Z))))) · (ƛ (ƛ ` (S Z) · (` (S Z) · ` Z))) · (ƛ (ƛ ` (S Z) · (` (S Z) · ` Z))) · (ƛ `suc ` Z) · `zero —→⟨ ξ-·₁ (ξ-·₁ (ξ-·₁ (β-ƛ V-ƛ))) ⟩ (ƛ (ƛ (ƛ (ƛ (ƛ ` (S Z) · (` (S Z) · ` Z))) · ` (S Z) · (` (S (S Z)) · ` (S Z) · ` Z)))) · (ƛ (ƛ ` (S Z) · (` (S Z) · ` Z))) · (ƛ `suc ` Z) · `zero —→⟨ ξ-·₁ (ξ-·₁ (β-ƛ V-ƛ)) ⟩ (ƛ (ƛ (ƛ (ƛ ` (S Z) · (` (S Z) · ` Z))) · ` (S Z) · ((ƛ (ƛ ` (S Z) · (` (S Z) · ` Z))) · ` (S Z) · ` Z))) · (ƛ `suc ` Z) · `zero —→⟨ ξ-·₁ (β-ƛ V-ƛ) ⟩ (ƛ (ƛ (ƛ ` (S Z) · (` (S Z) · ` Z))) · (ƛ `suc ` Z) · ((ƛ (ƛ ` (S Z) · (` (S Z) · ` Z))) · (ƛ `suc ` Z) · ` Z)) · `zero —→⟨ β-ƛ V-zero ⟩ (ƛ (ƛ ` (S Z) · (` (S Z) · ` Z))) · (ƛ `suc ` Z) · ((ƛ (ƛ ` (S Z) · (` (S Z) · ` Z))) · (ƛ `suc ` Z) · `zero) —→⟨ ξ-·₁ (β-ƛ V-ƛ) ⟩ (ƛ (ƛ `suc ` Z) · ((ƛ `suc ` Z) · ` Z)) · ((ƛ (ƛ ` (S Z) · (` (S Z) · ` Z))) · (ƛ `suc ` Z) · `zero) —→⟨ ξ-·₂ V-ƛ (ξ-·₁ (β-ƛ V-ƛ)) ⟩ (ƛ (ƛ `suc ` Z) · ((ƛ `suc ` Z) · ` Z)) · ((ƛ (ƛ `suc ` Z) · ((ƛ `suc ` Z) · ` Z)) · `zero) —→⟨ ξ-·₂ V-ƛ (β-ƛ V-zero) ⟩ (ƛ (ƛ `suc ` Z) · ((ƛ `suc ` Z) · ` Z)) · ((ƛ `suc ` Z) · ((ƛ `suc ` Z) · `zero)) —→⟨ ξ-·₂ V-ƛ (ξ-·₂ V-ƛ (β-ƛ V-zero)) ⟩ (ƛ (ƛ `suc ` Z) · ((ƛ `suc ` Z) · ` Z)) · ((ƛ `suc ` Z) · `suc `zero) —→⟨ ξ-·₂ V-ƛ (β-ƛ (V-suc V-zero)) ⟩ (ƛ (ƛ `suc ` Z) · ((ƛ `suc ` Z) · ` Z)) · `suc (`suc `zero) —→⟨ β-ƛ (V-suc (V-suc V-zero)) ⟩ (ƛ `suc ` Z) · ((ƛ `suc ` Z) · `suc (`suc `zero)) —→⟨ ξ-·₂ V-ƛ (β-ƛ (V-suc (V-suc V-zero))) ⟩ (ƛ `suc ` Z) · `suc (`suc (`suc `zero)) —→⟨ β-ƛ (V-suc (V-suc (V-suc V-zero))) ⟩ `suc (`suc (`suc (`suc `zero))) ∎) (done (V-suc (V-suc (V-suc (V-suc V-zero))))) _ = refl ``` We omit the proof that reduction is deterministic, since it is tedious and almost identical to the previous proof. #### Exercise `mul-example` (recommended) Using the evaluator, confirm that two times two is four. ``` -- Your code goes here ``` ## Intrinsic typing is golden Counting the lines of code is instructive. While this chapter covers the same formal development as the previous two chapters, it has much less code. Omitting all the examples, and all proofs that appear in Properties but not DeBruijn (such as the proof that reduction is deterministic), the number of lines of code is as follows: Lambda 216 Properties 235 DeBruijn 276 The relation between the two approaches approximates the golden ratio: extrinsically-typed terms require about 1.6 times as much code as intrinsically-typed. ## Unicode This chapter uses the following unicode: σ U+03C3 GREEK SMALL LETTER SIGMA (\Gs or \sigma) ₀ U+2080 SUBSCRIPT ZERO (\_0) ₃ U+20B3 SUBSCRIPT THREE (\_3) ₄ U+2084 SUBSCRIPT FOUR (\_4) ₅ U+2085 SUBSCRIPT FIVE (\_5) ₆ U+2086 SUBSCRIPT SIX (\_6) ₇ U+2087 SUBSCRIPT SEVEN (\_7) ≠ U+2260 NOT EQUAL TO (\=n)