1 The let-zl language
1.1 Syntax
The let-zl language has expressions defined as follows:
There are two kinds of literal expressions, integers and the empty list . Additionally, we build longer lists with , which is our traditional cons that creates a linked list node with first and rest. We have two elimination forms for integers, and . Additionally, we have elimination forms for lists, and . Finally, we have variables , and we have a form of sharing in , which binds to the value of in .
1.2 Dynamic semantics
Evaluation context give a grammar for where evaluation can take place. For example, suppose we want to reduce the term . We need to decompose that term into an evaluation context and a redex, so that they match one of the reduction rules above. We can do that: the evaluation context is , which matches the grammar of , and the redex in the hole is thus . This decomposition matches rule [plus], which converts it to . Then to perform another reduction, we decompose again, into evaluation context and redex . That converts to plugged back into the evaluation context, for . Then to perform one more reduction step, we decompose into the evaluation context and the redex , which converts to .
We define to be the reflexive, transitive closure of . That is, means that reduces to in zero or more steps.
The dynamic semantics of let-zl is now given by the evaluation function eval, defined as:
eval() =
if
As we discuss below, eval is partial for let-zl because there are errors that cause reduction to get “stuck.”
Exercise 1. Extend the language with Booleans. Besides Boolean literals, what do you think are essential operations? Extend the dynamic semantics with the necessary reduction rule(s) and evaluation context(s).
Exercise 2. Prove that for all values, = 0.
1.2.1 Errors
Can let-zl programs experience errors? What does it mean for a reduction semantics to have an error? Right now, there are no explicit, checked errors, but there are programs that don’t make sense. For example, . What do these non-sense terms do right now? They get stuck! That is, a term that has in the hole won’t reduce any further.
and .
and , where is an integer.
or where or is not an integer.
Any open term, that is, a term with a variable that is not bound by .
What do these stuck states mean? They might correspond to a real language executing an invalid instruction or some other kind of undefined behavior. This is no good, but there are several ways we could solve the problem.
We now update our evaluation function eval to take these errors into account:
eval() =
if
eval() =
if
Alas, eval is still partial, because there are stuck states that we haven’t converted to wrong states. (The other reason that eval could be partial is non-termination, but as we will prove, we don’t have that.) A second way to rule out stuck states is to impose a type system, which rules out programs with some kinds of errors. We can then prove that no programs admitted by the type system get stuck, which will make eval total for this language.
1.3 Static semantics
Exercise 3. Extend the type system to your language with Booleans.
Exercise 4 (Generic lists). Modify the type system as follows: instead of a single type for lists of s, allow , , and so on. How do you have to change the syntax of ? The typing rules?
1.3.1 Type safety
It will reduce in some number of steps to a value that also has type .
It will reduce in some number of steps to .
It will reduce forever.
Preservation: if has type and converts in one step to , then also has type .
Progress: if has a type , then either takes a conversion step or is a value.
1.3.1.1 Preservation
Before we start, we make an observation about how typing derivations must be formed.
If the term is a variable then = .
If the term is an integer then = .
If the term is then = .
If the term is or then = and and .
If the term is , then = and and
If the term is then there is some type such that and .
Proof. By inspection of the typing rules.
We want to prove that if a term has a type and takes a step, the resulting term also has a type. We can do this be considering the cases of the reduction relation and showing that each preserves the type. Alas, each rule involves evaluation contexts in the way of the action. Consequently, we’ll have to prove a lemma about evaluation contexts.
Lemma (Replacement). If , then there exists some type such that . Furthermore, for any other term such that , it is the case that .
Proof. By induction on the structure of :
If is , then = , so must be . Then since , we have that .
If , then the only typing rule that applies is [cons], which means that must be . Furthermore, by inversion of that rule it must be the case that and . By the induction hypothesis on the former, has some type , and furthermore, for any term that also has type , we have that . Then by applying rule [list], we have that .
If , then as in the previous case, the only typing rule that applies is [cons], which means that must be . It also means that must have type and must have type . Then by IH on the former, has a type , and furthermore, for any having type , . Then by reapplying rule [cons], we have that .
If , then the only typing rule that applies is [plus], which means that is . It also requires that and both have type . Then apply IH to the former, yielding that has some type . Furthermore, by the IH, for any other having type , we have that . Then reapplying rule [plus], we have that .
If or or , as in the previous case, m.m.
If (or ) then the only typing rule that applies is [car] (resp. [cdr]), which means that is (resp. ). Furthermore, rule [car] (resp. [cdr]) requires that must have type . Then apply IH to get that as well. Then as well. Then apply rule [car] (resp. [cdr]) to get that has type (resp. ).
If , then the only rule that applies is [let]. By that rule, must have some type , and . Then by the IH on the former, for some . Furthermore, for any other having type , the IH tells us that as well. Then we can reapply rule [let] to get .
QED.
There’s one more standard lemma we need before we can prove preservation:
Lemma (Substitution). If and then .
Proof. By induction on the typing derivation for ; by cases on the conclusion:
: Then is , and .
: Then is , and .
: Then we know that and . Then by the induction hypothesis, and . Then by rule [cons], we have that . But is , so .
: Then we know that and . Then by the induction hypothesis, and . Then apply rule [plus].
: as in the previous case.
: Then we know that . Then by IH, . And then by rule [car], .
: As in the previous case.
- : There are two possibilities, whether = or not:
First, consider the case where ≠ . Then we know that for some , and that . Then by the induction hypothesis, . Because ≠ , = . (This reordering could be proved correct in an “exchange” lemma, but we take it to be obviously correct from the typing rules. Exchange will be of more interest when linear type systems force us to get serious about contexts.) So we have that . Then by the induction hypothesis, . Then by rule [let].
If = then, as before, the induction hypothesis gives us that . By the assumption we know that . By inversion, we know that . But from the way environments work, we know that is the same as . Thus we know , which gives us the pieces to use the let rule to conclude that , which is almost what we need to finish this case. Consider what the substituion function does when the variables are equal: = = . That means, that the typing derivation we just proved, namely is the same as the one that finishes this case, and thus .
- : There are two possibilities, whether = or not:
If = , then is . Furthermore, this means that = . And we have from the premise that .
If ≠ , then is . Furthermore, we know that = = . Then .
QED.
Now we are ready to prove preservation:
Lemma (Preservation). If and then .
Proof. By cases on the reduction relation:
: By the replacement lemma, must have some type, and by inversion, that type must be . The result of the addition metafunction is also an integer with type . Then by replacement, .
: as in the previous case.
: By the replacement lemma, for some type . The only rule that applies is [car], which requires that = and . This types only by rule [cons], which requires that . Then by replacement, .
: By the replacement lemma, for some type . The only rule that applies is [cdr], which requires that = and . This types only by rule [cons], which requires that . Then by replacement, .
: By the replacement lemma, for some types . The only rule that applies is [let], which requires that for some such that . Then by the substitution lemma, . Then by replacement, .
QED.
1.3.1.2 Progress
Before we can prove progress, we need to classify values by their types.
Lemma (Canonical forms).
If is then is an integer literal .
If is , then either = or = where has type and has type .
Proof. By induction on the typing derivation of :
: Then is an integer literal.
: Then is the empty list.
: By the syntax of values it must be the case that is a value having type , and is a value having type .
: Vacuous, because not a value.
The remaining cases are all vacuous because they do not allow for value forms.
QED.
Lemma (Context replacement). If then . If then .
Proof. If then must be some redex in a hole: . Furthermore, it must take a step to some = . Then the same redex converts to the same contractum in any evaluation context, including .
If then must be some redex in a hole: which converts to . Then that same redex converts to in any evaluation context, including .
Lemma (Progress). If then term either converts or is a value.
Proof. By induction on the typing derivation; by cases on the conclusion:
: Then is a value.
: Then is a value.
: Then and . By the induction hypothesis, term either converts, or is a value. If converts to some term , then by the context replacement lemma. If converts to , then by the context replacement lemma. If is a value , then consider , which by the induction hypothesis either converts or is a value. If converts to a term , then by the context replacement lemma. If converts to , then by the context replacement lemma. Finally, if is a value then is a value .
: Then and . By the induction hypothesis, either converts or is a value. If converts to a term , then by the context replacement lemma. If converts to then by the context replacement lemma. If is a value , then consider , which by the induction hypothesis either converts or is a value. If converts to a term , then by the context replacement lemma. If converts to , then by the context replacement lemma. Otherwise, is a value . By the canonical forms lemma, is an integer and is an integer . Thus, we can take the step .
: As in the previous case.
: Then . By the induction hypothesis, either converts or is a value. If it converts to a term , then by the context replacement lemma. If it converts to , then by the context replacement lemma. Otherwise, is a value. By the canonical forms lemma, it has the form , so we can take a step .
: As in the previous case, but reducing to .
: Vacuous.
: Then and for some . Then by the induction hypothesis, either converts or is a value. If converts to a term , then by the context replacement lemma. If converts to then by the context replacement lemma. Otherwise, is a value , and .
QED.
Exercise 5. Prove progress and preservation for your language extended with Booleans.
Exercise 6. Prove progress and preservation for your language extended with generic lists.
Exercise 7. Are the previous two exercises orthogonal? How do they interact or avoid interaction?
1.4 Termination
Now let’s prove a rather strong property about a rather weak language.
Theorem (Size is work). Suppose and = k. Then either reduces to a value or goes wrong in k or fewer steps.
Proof. This proof uses induction, but it uses induction on the set ℕ × ℕ, using a lexicographic ordering. That is, we consider the first natural number to be the number of nodes in the given (when viewed as a tree) and the second one to be . The lexicographic order is well-founded, and so we can use induction when we have a term where the is strictly less than the given one, or when is the same as the given one, but the number of nodes is strictly smaller.
: Then k = 0, and reduces to value in 0 steps.
: Also k = 0.
. Then by inversion of [cons], and . Let j be the size of ; then the size of is k – j. We can use induction on both and , because they both have strictly fewer nodes that , and and are both less than or equal to . (With the exception of the case, the justification for induction will be the same as this one in all the other cases.) Then by induction, reduces to a value or to in j or fewer steps. If it reduces to then by the context replacement lemma, also reduces to in j or fewer steps. Otherwise, consider the induction hypothesis on (size k – j); it must reduce to a value or to in k – j or fewer steps. If , then the whole thing goes wrong by context replacement. Otherwise, goes to in k or fewer steps.
. Then by inversion of the typing rule , both subterms have type . Let j be the size of ; then the size of is k – j – 1. Then by the induction hypothesis, each reduces to a value or goes wrong, in at most j and k – j – 1 steps respectively. If either goes wrong, then the whole term goes wrong because both and are evaluation contexts. Otherwise, by the canonical values lemma both values must be numbers and . Because in j or fewer steps, by context replacement in j or fewer steps. And because in k – j – 1 or fewer steps, by context replacement again in k – j – 1 or fewer steps. Then in one more step , which is a value. The total number of steps has been k or fewer.
: As in the previous case, m.m.
and : In either case, the subterm must have type by inversion of the typing rule. Furthermore, the size of must be k – 1. Then by the induction hypothesis, either reduces to a value or goes wrong in k – 1 or fewer steps. If it goes wrong then the whole term goes wrong. If it reduces to a value, then by preservation, that value also has type . (Note also that it also reduces to a value in the evaluation context .) Then by the canonical values lemma, that value must be either or for some values and . If the former then the whole term goes to in one more step by rule [car-nil] or rule [cdr-nil], respectively. If the latter, then it take one more step to or , respectively. In either case, k steps have transpired.
: Vacuous because open terms don’t type.
: By inversion, we know that for some type . And we know that . Let j be the size of ; then the size of is k – j – 1. We can use induction on because is less than and there are strictly few nodes. Thus, by induction on , we have that reduces to a value or goes wrong in j or fewer steps. If it goes wrong then the whole term goes wrong. If it reduces to a value , then by context replacement (and induction on the length of the reduction sequence), the whole term reduces in j or fewer steps. Then in one more step, . Note that because the size of a variable is 0 and so is the size of a value, the size of is the same as the size of , k – j – 1, which is strictly less than the size of . In this case, the number of nodes in might be many more than the number of nodes in because might be a long list. But we set up our induction using the lexicgraphic order so that we only need to consider the relative sizes of and , not the number of nodes them to justify induction. Now, by preservation, . So we an apply the induction hypothesis to , learning that it goes wrong or reaches a value in k – j – 1 or fewer steps. This yields a total of k or fewer steps.
QED.