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.