Homework 5:
[9 May 2005: I'm fixing a bug in the definition of the relations
below. In particular, note that EV[int] = {(i,i)} whereas before,
I had EV[int] = {(i,\k.k i)} which breaks down when we consider
the variable case. The definitions below should work out cleaner.
]
[22 March 2005: I've updated the definitions to make the
proof easier and more modular.]
Complete the proof of the logical-relations argument for the
correctness of CPS conversion by proving:
Thm: if G |- e : t, then for all (g1,g2) in EV[G],
(g1(e), g2(E[e])) in EC[t].
using the following definitions:
Typing:
G |- i : int
G |- x : G(x)
G |- e1 : t'->t G |- e2 : t'
-----------------------------
G |- e1 e2 : t'
G,x:t1 |- e : t2
---------------------
G |- \x:t1.e : t1->t2
CPS Machine (normal CBV-lambda calculus):
(\x:t.e) v -> e{v/x}
e1 -> e1'
---------------
e1 e2 -> e1' e2
e2 -> e2'
---------------
v1 e2 -> v1 e2'
Stack Machine:
F ::= [] e | v []
S ::= nil | F::S
-> ~~
~~~~ -> ~~~~
~~~~ -> <(v [])::S,e> (e not a value)
~~~~ -> <([] e2)::S,e1> (e1 not a value)
CPS Translation (Plotkin & Fisher):
V[int] = int
V[t1->t2] = V[t1] -> C[t2]
C[t] = (V[t] -> int) -> int
E[i] = \k.k i
E[v] = \k.k v
E[\x:t.e] = \k.k (\x:V[t].\c.(E[e] c))
E[e1 e2] = \k.E[e1](\v1.E[e2](\v2.v1 v2 k)
Definition of Relations:
EM = { (~~~~,e2) | ~~~~ => iff e2 => i }
EV[int] = { (i,i) }
EV[t1 -> t2] = { (v1,v2) |
forall (v1',v2') in EV[t1].(v1 v1', v2 v2') in EC[t2] }
EC[t] = { (e1,e2) |
forall (S,v) in EK[t].(~~~~,e2 v) in EM }
EK[t] = { (S,v) |
forall (v1,v2) in EV[t].(~~~~,v v2) in EM }
EV[G] = { (g1,g2) | forall x in Dom(G).(g1(x),g2(x)) in EV[G(x)] }
Intuitively, EM captures the set of equivalent stack machine configurations
and CPS terms. EV[t] captures the set of equivalent values for the two
machines, and EC[t] captures the set of equivalent computations.
Finally, EK[t] captures the equivalent continuations (or stacks or
contexts---whatever you want to call them.)
As values, a stack machine integer i is equivalent to the CPS machine
value i. A stack machine function \x.e1 is equivalent to the CPS function
\x.e2 if, when we supply them equivalent arguments v1' and v2' (at the
domain type), then we get equivalent computations (at the codomain
type.)
Two expressions (e1,e2) are equivalent computations at t if for any
equivalent continuations S and v, when we run e1 with stack S and run
e2 with continuation v, we get equivalent machines (i.e., the programs
produce the same integer answer.)
A stack S and a continuation \v.e are equivalent (at type t) if when
given equivalent values v1 and v2 (at type t), we can run ~~~~ and
\v.e v2 and get the same integer answer out.
Although the relations seem to be recursive, they are in fact
inductively generated because (a) EM and EV[int] are defined
out-right, without appealing to any of the other relations, (b)
EV[t1->t2] is defined in terms of EV[t1] and EC[t2] for t1 and t2 that
are strictly smaller than than t (i.e., eventually we get down to just
a use of EM or EV[int].)
The theorem tells us that for any well-typed stack expression e,
when we CPS translate e, we get out an equivalent computation.
If we also establish the lemma:
Lemma: (nil,\v.v) in EK[int]
that is, the empty stack and the identity function are
equivalent continuations (at the type int), then w have as a
corollary of our main theorem:
Corrollary: If |- e : int then ->* iff
E[e](\v.v) ->* i.
which finally establishes the correctness of the CPS conversion.~~