1 Gaining control over order of evaluation
2 ----------------------------------------
4 We know that evaluation order matters. We're beginning to learn how
5 to gain some control over order of evaluation (think of Jim's abort handler).
6 We continue to reason about order of evaluation.
8 A lucid discussion of evaluation order in the
9 context of the lambda calculus can be found here:
10 [Sestoft: Demonstrating Lambda Calculus Reduction](http://www.itu.dk/~sestoft/papers/mfps2001-sestoft.pdf).
11 Sestoft also provides a lovely on-line lambda evaluator:
12 [Sestoft: Lambda calculus reduction workbench](http://www.itu.dk/~sestoft/lamreduce/index.html),
13 which allows you to select multiple evaluation strategies,
14 and to see reductions happen step by step.
16 Evaluation order matters
17 ------------------------
19 We've seen this many times. For instance, consider the following
20 reductions. It will be convenient to use the abbreviation `w =
21 \x.xx`. I'll indicate which lambda is about to be reduced with a *
30 Done! We have a normal form. But if we reduce using a different
31 strategy, things go wrong:
45 As a second reminder of when evaluation order matters, consider using
46 `Y = \f.(\h.f(hh))(\h.f(hh))` as a fixed point combinator to define a recursive function:
50 (\f.(\h.f(hh))(\h.f(hh))) (\f n. blah)
52 (\f.f((\h.f(hh))(\h.f(hh)))) (\f n. blah)
54 (\f.f(f((\h.f(hh))(\h.f(hh))))) (\f n. blah)
56 (\f.f(f(f((\h.f(hh))(\h.f(hh)))))) (\f n. blah)
59 And we never get the recursion off the ground.
62 Using a Continuation Passing Style transform to control order of evaluation
63 ---------------------------------------------------------------------------
65 We'll present a technique for controlling evaluation order by transforming a lambda term
66 using a Continuation Passing Style transform (CPS), then we'll explore
67 what the CPS is doing, and how.
69 In order for the CPS to work, we have to adopt a new restriction on
70 beta reduction: beta reduction does not occur underneath a lambda.
71 That is, `(\x.y)z` reduces to `z`, but `\w.(\x.y)z` does not, because
72 the `\w` protects the redex in the body from reduction.
73 (A redex is a subform ...(\xM)N..., i.e., something that can be the
74 target of beta reduction.)
76 Start with a simple form that has two different reduction paths:
78 reducing the leftmost lambda first: `(\x.y)((\x.z)w) ~~> y`
80 reducing the rightmost lambda first: `(\x.y)((\x.z)w) ~~> (x.y)z ~~> y`
82 After using the following call-by-name CPS transform---and assuming
83 that we never evaluate redexes protected by a lambda---only the first
84 reduction path will be available: we will have gained control over the
85 order in which beta reductions are allowed to be performed.
87 Here's the CPS transform:
91 [MN] => \k.[M](\m.m[N]k)
93 Here's the result of applying the transform to our problem term:
96 \k.[\x.y](\m.m[(\x.z)w]k)
97 \k.(\k.k(\x.[y]))(\m.m(\k.[\x.z](\m.m[w]k))k)
98 \k.(\k.k(\x.y))(\m.m(\k.(\k.k(\x.z))(\m.mwk))k)
100 Because the initial `\k` protects the entire transformed term,
101 we can't perform any reductions. In order to see the computation
102 unfold, we have to apply the transformed term to a trivial
103 continuation, usually the identity function `I = \x.x`.
106 \k.[\x.y](\m.m[(\x.z)w]k) I
107 [\x.y](\m.m[(\x.z)w] I)
108 (\k.k(\x.y))(\m.m[(\x.z)w] I)
112 The application to `I` unlocks the leftmost functor. Because that
113 functor (`\x.y`) throws away its argument, we never need to expand the
114 CPS transform of the argument.
116 Compare with a call-by-value xform:
120 {MN} => \k.{M}(\m.{N}(\n.mnk))
122 This time the reduction unfolds in a different manner:
125 (\k.{\x.y}(\m.{(\x.z)w}(\n.mnk))) I
126 {\x.y}(\m.{(\x.z)w}(\n.mnI))
127 (\k.k(\x.{y}))(\m.{(\x.z)w}(\n.mnI))
128 {(\x.z)w}(\n.(\x.{y})nI)
129 (\k.{\x.z}(\m.{w}(\n.mnk)))(\n.(\x.{y})nI)
130 {\x.z}(\m.{w}(\n.mn(\n.(\x.{y})nI)))
131 (\k.k(\x.{z}))(\m.{w}(\n.mn(\n.(\x.{y})nI)))
132 {w}(\n.(\x.{z})n(\n.(\x.{y})nI))
133 (\k.kw)(\n.(\x.{z})n(\n.(\x.{y})nI))
134 (\x.{z})w(\n.(\x.{y})nI)
136 (\k.kz)(\n.(\x.{y})nI)
142 Both xforms make the following guarantee: as long as redexes
143 underneath a lambda are never evaluated, there will be at most one
144 reduction available at any step in the evaluation.
145 That is, all choice is removed from the evaluation process.
147 Questions and excercises:
149 1. Why is the CBN xform for variables `[x] = x' instead of something
152 2. Write an Ocaml function that takes a lambda term and returns a
153 CPS-xformed lambda term. You can use the following data declaration:
155 type form = Var of char | Abs of char * form | App of form * form;;
157 3. What happens (in terms of evaluation order) when the application
158 rule for CBN CPS is changed to `[MN] = \k.[N](\n.[M]nk)`? Likewise,
159 What happens when the application rule for CBV CPS is changed to
160 `{MN} = \k.{N}(\n.{M}(\m.mnk))`?
162 4. What happens when the application rules for the CPS xforms are changed to
165 [MN] = \k.{M}(\m.m{N}k)
166 {MN} = \k.[M](\m.[N](\n.mnk))
169 Thinking through the types
170 --------------------------
172 This discussion is based on [Meyer and Wand 1985](http://citeseer.ist.psu.edu/viewdoc/download?doi=10.1.1.44.7943&rep=rep1&type=pdf).
174 Let's say we're working in the simply-typed lambda calculus.
175 Then if the original term is well-typed, the CPS xform will also be
176 well-typed. But what will the type of the transformed term be?
178 The transformed terms all have the form `\k.blah`. The rule for the
179 CBN xform of a variable appears to be an exception, but instead of
180 writing `[x] => x`, we can write `[x] => \k.xk`, which is
181 eta-equivalent. The `k`'s are continuations: functions from something
182 to a result. Let's use σ as the result type. The each `k` in
183 the transform will be a function of type ρ --> σ for some
186 We'll need an ancilliary function ': for any ground type a, a' = a;
187 for functional types a->b, (a->b)' = a' -> (b' -> o) -> o.
189 Call by name transform
193 [x] => \k.xk [a] => (a'->o)->o
194 [\xM] => \k.k(\x[M]) [a->b] => ((a->b)'->o)->o
195 [MN] => \k.[M](\m.m[N]k) [b] => (b'->o)->o
197 Remember that types associate to the right. Let's work through the
198 application xform and make sure the types are consistent. We'll have
209 [M]:((a->b)'->o)->o = ((a'->(b'->o)->o)->o)->o
213 Note that even though the transform uses the same symbol for the
214 translation of a variable, in general it will have a different type in
215 the transformed term.