Lambda Calculus
---------------
Last time, we introduced the simply-typed lambda calculus and its type
system. We proved a type safety theorem, showing that "well-typed
programs don't go wrong". However, we noted that we couldn't find a
type t such that OMEGA = (\f:??. (f f)) (\f:??. (f f)) is a
well-typed expression of type e. Today we will confirm that, in
contrast to IMP and the untyped lambda calculus, the simply-typed
lambda calculus does not admit (well-typed) expressions whose
evaluation diverges.
* Small-step, Call-by-value *
First, let's review the small-step, call-by-value operational
semantics for the lambda calculus:
(Arith) i1 bop i2 -> i (i == i1 bop i2)
e1 -> e1'
(ArithL) -------------------------
e1 bop e2 -> e1' bop e2
e2 -> e2'
(ArithR) -------------------------
v1 bop e2 -> v1 bop e2'
(IfT) if i then e1 else e2 -> e1 (i != 0)
(IfF) if 0 then e1 else e2 -> e2
e -> e'
(IfC) ------------------------------------------------
if e then e1 else e2 -> if e0' then e1 else e2
e1 -> e1'
(Oper) -----------------
e1 e2 -> e1' e2
e2 -> e2'
(Arg) -----------------
v1 e2 -> v1 e2'
(Beta) (\x:t. e1) v2 -> e1[v2/x]
* Large-step, Call-by-value *
We can also give a large-step, call-by-value operational semantics for
the lambda calculus.
(Int) i => i
e1 => i1 e2 => i2
(Arith) --------------------- (i == i1 bop i2)
e1 bop e2 => i
e => i e1 => v
(IfT) --------------------------- (i != 0)
if e then e1 else e2 => v
e => 0 e2 => v
(IfF) ---------------------------
if e then e1 else e2 => v
(Abs) \x:t e' => \x:t. e'
e1 => \x:t e' e2 => v2 e'[v2/x] => v
(Beta) ------------------------------------------
e1 e2 => v
Trivial Exercise:
What changes are necessary to give a large-step, call-by-name
operational semantics for the lambda calculus?
As with IMP, we took the small-step semantics as the definition of
what it means for a lambda calculus program to evaluate, so we are
obligated to prove that the large-step semantics coincide with the
small-step semantics. That is we (and by we, I mean you) are
obligated to prove that:
Theorem:
1. e => v' implies e ->* v'
2. e ->* v' implies e => v'
Proof: Excercise
Having this theorem in hand, we can jump back and forth between the
small-step and the large-step semantics depending on which is more
convenient for the task at hand. In particular, note that if we wish
to prove some theorem about the evaluation of e to a value, we can
either use
I) e ->* v,
which will likely incur an induction on n (the number of steps)
followed by induction on the (9 rules for the) small-step
relation, or
II) e => v,
which will only incur an induction on the (6 rules for the)
large-step relation.
But, let's move on to proving that the evaluation of all well-typed
lambda calculus expressions must terminate.
* Proof of Termination *
To be concrete, we state the theorem we are hoping to prove:
Theorem [Normalization]
If e is a closed expression of type t ( |- e : t ),
then e is normalizable ( e ->* v / e => v).
Given everything we've seen up to this point, we would be well-advised
to attempt proving this theorem by induction. Unfortunately, a direct
application of induction will fail. Let's see why:
Proof Attempt of Normalization:
Proceed by induction on the structure of the expression e.
Case (e = e1 e2): Note that we have the derivation:
|- e1 : ta -> t |- e2 : ta
-----------------------------
|- e1 e2 : t
Apply induction to e1; applying the result to |- e1 : ta -> t, we
conclude that e1 => v1. Applying Preservation and Canonical Forms,
we conclude that v1 = \x:ta. e'.
Apply induction to e2; applying the result to |- e2 : ta, we
conclude that e2 => v2.
Now, we want to show that
e1 => \x:t e' e2 => v2 e'[v2/x] => v
(Beta) ------------------------------------------
e1 e2 => v
But e'[v2/x] is not a sub-expression of e.
XXX
We will run into similar problems attempting to induct on
* the size of the expression e
because the size of e'[v2/x] may be larger than the size of e,
* the structure of the type t,
because the type ta -> t is not a sub-type of t (!! Note: we mean
"sub-type" in the syntactic sense, not in the subtyping sense. !!),
* the size of the type t,
because the size of the type ta -> t is larger than the size of t,
* the structure of the derivation |- e : t,
because the derivation of |- e'[v2/x] : t, which exists by the
Substitution Lemma, is not a sub-derivation of |- e : t,
* the size of the derivation of |- e : t,
because the size of the derivation of |- e'[v2/x] : t may be larger
than the size of the derivation of |- e : t,
To make progress, we need to introduce another proof technique: a
logical relations argument. This technique will provide us with a
strong enough induction hypothesis to complete our proof.
* Logical Relations *
Logical relations arguments start by defining a family of relations,
indexed by types. In our case, the relations are unary:
V[int] = { v | |- v : int } = { i }
V[t1 -> t2] = { v | |- v : t1 -> t2 and
All v' in V[t1]. v v' in E[t2] }
E[t] = { e | |- e : t and e => v and v in V[t] }
Note that V[t] subset E[t] subset { e | |- e : t }.
Notice that we've rigged the definition so that E[t] includes the
well-typed expressions of type t that terminate and, futhermore, if it
terminates with a lambda-abstraction, that abstraction terminates when
applied to a value. This gets us around the trickyness with
application, but we'll need to show that we haven't left out any
well-typed expressions.
Lemma:
If e in E[t],
then e is normalizable ( e => v / e ->* v ).
Proof:
Immediate from the definition of E[t].
Now, we want to prove that every closed expression of type t is a
member of E[t]. We'll want to proceed by induction on the
derivation |- e : t. The one remaining obstacle is the fact that in
the (Abs) case, our only sub-derivation is of the form x:t1 |- e' : t2.
This problem is easily solved by strengthening the lemma; rather than
prove a statement for all closed expressions, we prove the statement
for all compatible substitutions that close an open expression of type t.
We extend V[] to typing contexts in the following manner:
G[.] = { {} }
G[x:t,G] = { g[x |-> v] | g in G[G] and v in V[t] }
With this definition in hand, we prove our lemma.
Lemma:
If G |- e : t and g in G[G],
then g(e) in E[t].
Proof:
Note that |- g(e) : t by the Substitution Lemma.
Proceed by induction on the derivation G |- e : t.
Case (Var): Note that e is a variable (with e = x) and we have the
derivation:
(Var) G1,x:t,G2 |- x : t
Note that g(x) = v in V[t] subset E[t].
Case (Abs): Note that e is an abstraction (with e = \x:t1. e'
and t = t1 -> t2) and we have the derivation:
G,x:t1 |- e' : t2
(Abs) ---------------------------
G |- \x:t1. e' : t1 -> t2
Note that g(\x:t1. e') = \x:t1. g(e') (!! The BVC ensures that x
isn't in the domain of G or g. !!)
We have \x:t1. g(e') => \x:t1. g(e').
It remains to show that \x:t1. g(e') in V[t1 -> t2].
Consider arbitrary v' in V[t1].
We are required to show that (\x:t1. g(e')) v' in E[t2].
Note that
|- \x:t1. g(e') : t1 -> t2 |- v' : t1
(App) -----------------------------------------
|- (\x:t1. g(e')) v' : t2
Apply induction to G,x:t1 |- e' : t2 and g[x |-> v'] in G[G,x:t1];
we conclude that g[x |-> v'](e') in E[t2].
Hence, g[e |-> v'](e') => v'' and v'' in V[t2].
Furthermore,
\x:t1. g(e') => \x:t1. g(e') v' => v' g(e')[v'/x] => v''
(Beta) --------------------------------------------------------------
(\x:t1. g(e')) v' => v''
and v'' in V[t2].
Hence, (\x:t1. g(e')) v' in E[t2].
Therefore, \x:t1. g(e') in V[t1 -> t2].
Hence, g(\x:t1. e') in E[t1 -> t2].
Case (App): Note that e is an application (with e = e1 e2) and we
have the derivation:
G |- e1 : ta -> t G |- e2 : ta
(App) ----------------------------------
G |- e1 e2 : t
Note that g(e1 e2) = g(e1) g(e2).
Apply induction to G |- e1 : ta -> t and g in G[G];
we conclude that g(e1) in E[ta -> t].
Therefore, g(e1) => v1 and v1 in V[ta -> t].
By Canonical Forms, we conclude that v1 = \x:ta. e'.
Apply induction to G |- e2 : ta and g in G[G];
we conclude that g(e2) in E[ta].
Therefore, g(e2) => v2 and v2 in V[ta].
Note that \x:ta. e' in V[ta -> t] and v2 in V[ta]
implies that (\x:ta. e') v2 in E[t].
Hence, (\x:ta. e') v2 => v' and v' in V[t].
Therefore, we have the derivation:
\x:ta. e' => \x:ta. e' v2 => v2 e'[v2/x] => v'
(Beta) ----------------------------------------------------
(\x:ta. e') v2 => v'
Finally,
g(e1') => \x:ta. e' g(e2) => v2 e'[v2/x] => v'
(Beta) ---------------------------------------------------
g(e1) g(e2) => v'
and v' in V[t].
Hence, g(e) = g(e1) g(e2) in E[t].
Case (Int,Binop,If): ...
QED
Corollary:
If |- e : t,
then e in E[t].
Exercise:
Prove the Normalization Theorem for the call-by-name operational
semantics.
Exercise:
Pierce proves normalization using the following predicates:
E'[int] = { e | |- e : int and e ->* v }
E'[t1 -> t2] = { e | |- e : t1 -> t2 and e ->* v and
All e' in E'[t1]. e e' in E'[t2] }
Show that these definitions are equivalent to the ones above; i.e.,
prove that forall t, E[t] = E'[t].
Hint: There is an easy proof and a hard proof.
* Extensions *
Chapter 11 of Pierce demonstrates a number of simple extensions to the
simply-typed lambda calculus. Time permitting, we'll look at:
+ Unit +
t := ... | Unit
e := ... | #u
+ Product / Tuples / Record +
t := ... | t1 * t2
e := ... | | e.1 | e.2
t := ... | t1 * ... * tn
e := ... | | e.i
t := ... | {l1:t1, ..., ln:tn}
e := ... | {l1 = t1, ..., ln = tn} | e.l
+ Sum / Variants +
t := ... | t1 + t2
e := ... | inl e | inr e | case e of inl x => e1 | inr y => e2
t := ... | [l1:t1,...,ln:tn]
e := ... | [l=e] | case e of [l1=x1] => e1 | ... | [ln=xn] => en
+ General recursion +
e := ... | fix e
References:
Pierce: Ch 12, Ch 11