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.