1 [[!toc]]
3 #Q: How do you know that every term in the untyped lambda calculus has a fixed point?#
5 A: That's easy: let `T` be an arbitrary term in the lambda calculus.  If
6 `T` has a fixed point, then there exists some `X` such that `X <~~>
7 TX` (that's what it means to *have* a fixed point).
9 <pre><code>let L = \x. T (x x) in
10 let X = L L in
11 X &equiv; L L &equiv; (\x. T (x x)) L ~~> T (L L) &equiv; T X
12 </code></pre>
14 Please slow down and make sure that you understand what justified each
15 of the equalities in the last line.
17 #Q: How do you know that for any term `T`, `Y T` is a fixed point of `T`?#
19 A: Note that in the proof given in the previous answer, we chose `T`
20 and then set <code>X &equiv; L L &equiv; (\x. T (x x)) (\x. T (x x))</code>.  If we abstract over
21 `T`, we get the Y combinator, `\T. (\x. T (x x)) (\x. T (x x))`.  No matter
22 what argument `T` we feed `Y`, it returns some `X` that is a fixed point
23 of `T`, by the reasoning in the previous answer.
25 #Q: So if every term has a fixed point, even `Y` has fixed point.#
27 A: Right:
29 <pre><code>let Y = \T. (\x. T (x x)) (\x. T (x x)) in
30 Y Y
31 &equiv;   \T. (\x. T (x x)) (\x. T (x x)) Y
32 ~~> (\x. Y (x x)) (\x. Y (x x))
33 ~~> Y ((\x. Y (x x)) (\x. Y (x x)))
34 ~~> Y (Y ((\x. Y (x x)) (\x. Y (x x))))
35 ~~> Y (Y (Y (...(Y (Y Y))...)))
36 </code></pre>
39 #Q: Ouch!  Stop hurting my brain.#
41 A: Is that a question?
43 Let's come at it from the direction of arithmetic.  Recall that we
44 claimed that even `succ`---the function that added one to any
45 number---had a fixed point.  How could there be an X such that X = X+1?
46 That would imply that
48     X <~~> succ X <~~> succ (succ X) <~~> succ (succ (succ X)) <~~> succ (... (succ X)...)
50 In other words, the fixed point of `succ` is a term that is its own
51 successor.  Let's just check that `X = succ X`:
53 <pre><code>let succ = \n s z. s (n s z) in
54 let X = (\x. succ (x x)) (\x. succ (x x)) in
55 succ X
56 &equiv;   succ ( (\x. succ (x x)) (\x. succ (x x)) )
57 ~~> succ (succ ( (\x. succ (x x)) (\x. succ (x x)) ))
58 &equiv;   succ (succ X)
59 </code></pre>
61 You should see the close similarity with `Y Y` here.
64 #Q. So `Y` applied to `succ` returns a number that is not finite!#
66 A. Yes!  Let's see why it makes sense to think of `Y succ` as a Church
67 numeral:
69 <pre><code>[same definitions]
70 succ X
71 &equiv;    (\n s z. s (n s z)) X
72 ~~>  \s z. s (X s z)
73 <~~> succ (\s z. s (X s z)) ; using fixed-point reasoning
74 &equiv;    (\n s z. s (n s z)) (\s z. s (X s z))
75 ~~>  \s z. s ((\s z. s (X s z)) s z)
76 ~~>  \s z. s (s (X s z))
77 </code></pre>
79 So `succ X` looks like a numeral: it takes two arguments, `s` and `z`,
80 and returns a sequence of nested applications of `s`...
82 You should be able to prove that `add 2 (Y succ) <~~> Y succ`,
83 likewise for `mul`, `sub`, `pow`.  What happens if we try `sub (Y
84 succ) (Y succ)`?  What would you expect infinity minus infinity to be?
85 (Hint: choose your evaluation strategy so that you add two `s`s to the
86 first number for every `s` that you add to the second number.)
88 This is amazing, by the way: we're proving things about a term that
89 represents arithmetic infinity.
91 It's important to bear in mind the simplest term in question is not
92 infinite:
94         Y succ = (\f. (\x. f (x x)) (\x. f (x x))) (\n s z. s (n s z))
96 The way that infinity enters into the picture is that this term has
97 no normal form: no matter how many times we perform beta reduction,
98 there will always be an opportunity for more beta reduction.  (Lather,
99 rinse, repeat!)
102 #Q. That reminds me, what about [[evaluation order]]?#
104 A. For a recursive function that has a well-behaved base case, such as
105 the factorial function, evaluation order is crucial.  In the following
106 computation, we will arrive at a normal form.  Watch for the moment at
107 which we have to make a choice about which beta reduction to perform
108 next: one choice leads to a normal form, the other choice leads to
109 endless reduction:
111 <pre><code>let prefact = \f n. iszero n 1 (mul n (f (pred n))) in
112 let fact = Y prefact in
113 fact 2
114 &equiv;   [(\f. (\x. f (x x)) (\x. f (x x))) prefact] 2
115 ~~> [(\x. prefact (x x)) (\x. prefact (x x))] 2
116 ~~> [prefact ((\x. prefact (x x)) (\x. prefact (x x)))] 2
117 ~~> [prefact (prefact ((\x. prefact (x x)) (\x. prefact (x x))))] 2
118 &equiv;   [ (\f n. iszero n 1 (mul n (f (pred n)))) (prefact ((\x. prefact (x x)) (\x. prefact (x x))))] 2
119 ~~> [\n. iszero n 1 (mul n ([prefact ((\x. prefact (x x)) (\x. prefact (x x)))] (pred n)))] 2
120 ~~> iszero 2 1 (mul 2 ([prefact ((\x. prefact (x x)) (\x. prefact (x x)))] (pred 2)))
121 ~~> mul 2 ([prefact ((\x. prefact (x x)) (\x. prefact (x x)))] 1)
122 ...
123 ~~> mul 2 (mul 1 ([prefact ((\x. prefact (x x)) (\x. prefact (x x)))] 0))
124 &equiv;   mul 2 (mul 1 (iszero 0 1 (mul 1 ([prefact ((\x. prefact (x x)) (\x. prefact (x x)))] (pred 0)))))
125 ~~> mul 2 (mul 1 1)
126 ~~> mul 2 1
127 ~~> 2
128 </code></pre>
130 The crucial step is the third from the last.  We have our choice of
131 either evaluating the test `iszero 0 1 ...`, which evaluates to `1`,
132 no matter what the ... contains;
133 or we can evaluate the `Y` pump, `(\x. prefact (x x)) (\x. prefact (x x))`, to
134 produce another copy of `prefact`.  If we postpone evaluting the
135 `iszero` test, we'll pump out copy after copy of `prefact`, and never
136 realize that we've bottomed out in the recursion.  But if we adopt a
137 leftmost/call-by-name/normal-order evaluation strategy, we'll always
138 start with the `iszero` predicate, and only produce a fresh copy of
139 `prefact` if we are forced to.
142 #Q.  You claimed that the Ackermann function couldn't be implemented using our primitive recursion techniques (such as the techniques that allow us to define addition and multiplication).  But you haven't shown that it is possible to define the Ackermann function using full recursion.#
145 A. OK:
147         A(m,n) =
148                 | when m == 0 -> n + 1
149                 | else when n == 0 -> A(m-1,1)
150                 | else -> A(m-1, A(m,n-1))
152         let A = Y (\A m n. iszero m (succ n) (iszero n (A (pred m) 1) (A (pred m) (A m (pred n)))))
154 So for instance:
156         A 1 2
157         ~~> A 0 (A 1 1)
158         ~~> A 0 (A 0 (A 1 0))
159         ~~> A 0 (A 0 (A 0 1))
160         ~~> A 0 (A 0 2)
161         ~~> A 0 3
162         ~~> 4
164 `A 1 x` is to `A 0 x` as addition is to the successor function;
165 `A 2 x` is to `A 1 x` as multiplication is to addition;
166 `A 3 x` is to `A 2 x` as exponentiation is to multiplication---
167 so `A 4 x` is to `A 3 x` as hyper-exponentiation is to exponentiation...
169 #Q. What other questions should I be asking?#
171 *    What is it about the variant fixed-point combinators that makes
172      them compatible with a call-by-value evaluation strategy?
174 *    How do you know that the Ackermann function can't be computed
175      using primitive recursion techniques?
177 *    What *exactly* is primitive recursion?
179 *    I hear that `Y` delivers the *least* fixed point.  Least
180      according to what ordering?  How do you know it's least?
181      Is leastness important?
185 #Sets#
187 You're now already in a position to implement sets: that is, collections with
188 no intrinsic order where elements can occur at most once. Like lists, we'll
189 understand the basic set structures to be *type-homogenous*. So you might have
190 a set of integers, or you might have a set of pairs of integers, but you
191 wouldn't have a set that mixed both types of elements. Something *like* the
192 last option is also achievable, but it's more difficult, and we won't pursue it
193 now. In fact, we won't talk about sets of pairs, either. We'll just talk about
194 sets of integers. The same techniques we discuss here could also be applied to
195 sets of pairs of integers, or sets of triples of booleans, or sets of pairs
196 whose first elements are booleans, and whose second elements are triples of
197 integers. And so on.
199 (You're also now in a position to implement *multi*sets: that is, collections
200 with no intrinsic order where elements can occur multiple times: the multiset
201 {a,a} is distinct from the multiset {a}. But we'll leave these as an exercise.)
203 The easiest way to implement sets of integers would just be to use lists. When
204 you "add" a member to a set, you'd get back a list that was either identical to
205 the original list, if the added member already was present in it, or consisted
206 of a new list with the added member prepended to the old list. That is:
208         let empty_set = empty  in
209         ; see the library for definitions of any and eq
210         let make_set = \new_member old_set. any (eq new_member) old_set
211                                                 ; if any element in old_set was eq new_member
212                                                 old_set
213                                                 ; else
214                                                 make_list new_member old_set
216 Think about how you'd implement operations like `set_union`,
217 `set_intersection`, and `set_difference` with this implementation of sets.
219 The implementation just described works, and it's the simplest to code.
220 However, it's pretty inefficient. If you had a 100-member set, and you wanted
221 to create a set which had all those 100-members and some possibly new element
222 `e`, you might need to check all 100 members to see if they're equal to `e`
223 before concluding they're not, and returning the new list. And comparing for
224 numeric equality is a moderately expensive operation, in the first place.
226 (You might say, well, what's the harm in just prepending `e` to the list even
227 if it already occurs later in the list. The answer is, if you don't keep track
228 of things like this, it will likely mess up your implementations of
229 `set_difference` and so on. You'll have to do the book-keeping for duplicates
230 at some point in your code. It goes much more smoothly if you plan this from
231 the very beginning.)
233 How might we make the implementation more efficient? Well, the *semantics* of
234 sets says that they have no intrinsic order. That means, there's no difference
235 between the set {a,b} and the set {b,a}; whereas there is a difference between
236 the *list* `[a;b]` and the list `[b;a]`. But this semantic point can be respected
237 even if we *implement* sets with something ordered, like list---as we're
238 already doing. And we might *exploit* the intrinsic order of lists to make our
239 implementation of sets more efficient.
241 What we could do is arrange it so that a list that implements a set always
242 keeps in elements in some specified order. To do this, there'd have *to be*
243 some way to order its elements. Since we're talking now about sets of numbers,
244 that's easy. (If we were talking about sets of pairs of numbers, we'd use
245 "lexicographic" ordering, where `(a,b) < (c,d)` iff `a < c or (a == c and b <
246 d)`.)
248 So, if we were searching the list that implements some set to see if the number
249 `5` belonged to it, once we get to elements in the list that are larger than `5`,
250 we can stop. If we haven't found `5` already, we know it's not in the rest of the
251 list either.
253 >       *Comment*: This is an improvement, but it's still a "linear" search through the list.
254 There are even more efficient methods, which employ "binary" searching. They'd
255 represent the set in such a way that you could quickly determine whether some
256 element fell in one half, call it the left half, of the structure that
257 implements the set, if it belonged to the set at all. Or that it fell in the
258 right half, it it belonged to the set at all. And then the same sort of
259 determination could be made for whichever half you were directed to. And then
260 for whichever quarter you were directed to next. And so on. Until you either
261 found the element or exhausted the structure and could then conclude that the
262 element in question was not part of the set. These sorts of structures are done
263 using [binary trees](/implementing_trees).
266 #Aborting a search through a list#
268 We said that the sorted-list implementation of a set was more efficient than
269 the unsorted-list implementation, because as you were searching through the
270 list, you could come to a point where you knew the element wasn't going to be
271 found. So you wouldn't have to continue the search.
273 If your implementation of lists was, say v1 lists plus the Y-combinator, then
274 this is exactly right. When you get to a point where you know the answer, you
275 can just deliver that answer, and not branch into any further recursion. If
276 you've got the right evaluation strategy in place, everything will work out
277 fine.
279 But what if we wanted to use v3 lists instead?
281 >       Why would we want to do that? The advantage of the v3 lists and v3 (aka
282 "Church") numerals is that they have their recursive capacity built into their
283 very bones. So for many natural operations on them, you won't need to use a fixed
284 point combinator.
286 >       Why is that an advantage? Well, if you use a fixed point combinator, then
287 the terms you get won't be strongly normalizing: whether their reduction stops
288 at a normal form will depend on what evaluation order you use. Our online
289 [[lambda evaluator]] uses normal-order reduction, so it finds a normal form if
290 there's one to be had. But if you want to build lambda terms in, say, Scheme,
291 and you wanted to roll your own recursion as we've been doing, rather than
292 relying on Scheme's native `let rec` or `define`, then you can't use the
293 fixed-point combinators `Y` or <code>&Theta;</code>. Expressions using them
294 will have non-terminating reductions, with Scheme's eager/call-by-value
295 strategy. There are other fixed-point combinators you can use with Scheme (in
296 the [week 3 notes](/week3/#index7h2) they were <code>Y&prime;</code> and
297 <code>&Theta;&prime;</code>. But even with them, evaluation order still
298 matters: for some (admittedly unusual) evaluation strategies, expressions using
299 them will also be non-terminating.
301 >       The fixed-point combinators may be the conceptual stars. They are cool and
302 mathematically elegant. But for efficiency and implementation elegance, it's
303 best to know how to do as much as you can without them. (Also, that knowledge
304 could carry over to settings where the fixed point combinators are in principle
305 unavailable.)
308 So again, what if we're using v3 lists? What options would we have then for
309 aborting a search or list traversal before it runs to completion?
311 Suppose we're searching through the list `[5;4;3;2;1]` to see if it
312 contains the number `3`. The expression which represents this search would have
313 something like the following form:
315         ..................<eq? 1 3>  ~~>
316         .................. false     ~~>
317         .............<eq? 2 3>       ~~>
318         ............. false          ~~>
319         .........<eq? 3 3>           ~~>
320         ......... true               ~~>
321         ?
323 Of course, whether those reductions actually followed in that order would
324 depend on what reduction strategy was in place. But the result of folding the
325 search function over the part of the list whose head is `3` and whose tail is `[2;
326 1]` will *semantically* depend on the result of applying that function to the
327 more rightmost pieces of the list, too, regardless of what order the reduction
328 is computed by. Conceptually, it will be easiest if we think of the reduction
329 happening in the order displayed above.
331 Once we've found a match between our sought number `3` and some member of
332 the list, we'd like to avoid any further unnecessary computations and just
333 deliver the answer `true` as "quickly" or directly as possible to the larger
334 computation in which the search was embedded.
336 With a Y-combinator based search, as we said, we could do this by just not
337 following a recursion branch.
339 But with the v3 lists, the fold is "pre-programmed" to continue over the whole
340 list. There is no way for us to bail out of applying the search function to the
341 parts of the list that have head `4` and head `5`, too.
343 We *can* avoid *some* unneccessary computation. The search function can detect
344 that the result we've accumulated so far during the fold is now `true`, so we
345 don't need to bother comparing `4` or `5` to `3` for equality. That will simplify the
346 computation to some degree, since as we said, numerical comparison in the
347 system we're working in is moderately expensive.
349 However, we're still going to have to traverse the remainder of the list. That
350 `true` result will have to be passed along all the way to the leftmost head of
351 the list. Only then can we deliver it to the larger computation in which the
352 search was embedded.
354 It would be better if there were some way to "abort" the list traversal. If,
355 having found the element we're looking for (or having determined that the
356 element isn't going to be found), we could just immediately stop traversing the
357 list with our answer. **Continuations** will turn out to let us do that.
359 We won't try yet to fully exploit the terrible power of continuations. But
360 there's a way that we can gain their benefits here locally, without yet having
361 a fully general machinery or understanding of what's going on.
363 The key is to recall how our implementations of booleans and pairs worked.
364 Remember that with pairs, we supply the pair "handler" to the pair as *an
365 argument*, rather than the other way around:
367         pair (\x y. add x y)
369 or:
371         pair (\x y. x)
373 to get the first element of the pair. Of course you can lift that if you want:
375 <pre><code>extract_fst &equiv; \pair. pair (\x y. x)</code></pre>
377 but at a lower level, the pair is still accepting its handler as an argument,
378 rather than the handler taking the pair as an argument. (The handler gets *the
379 pair's elements*, not the pair itself, as arguments.)
381 >       *Terminology*: we'll try to use names of the form `get_foo` for handlers, and
382 names of the form `extract_foo` for lifted versions of them, that accept the
383 lists (or whatever data structure we're working with) as arguments. But we may
384 sometimes forget.
386 The v2 implementation of lists followed a similar strategy:
388         v2list (\h t. do_something_with_h_and_t) result_if_empty
390 If the `v2list` here is not empty, then this will reduce to the result of
391 supplying the list's head and tail to the handler `(\h t.
392 do_something_with_h_and_t)`.
394 Now, what we've been imagining ourselves doing with the search through the v3
395 list is something like this:
398         larger_computation (search_through_the_list_for_3) other_arguments
400 That is, the result of our search is supplied as an argument (perhaps together
401 with other arguments) to the "larger computation". Without knowing the
402 evaluation order/reduction strategy, we can't say whether the search is
403 evaluated before or after it's substituted into the larger computation. But
404 semantically, the search is the argument and the larger computation is the
405 function to which it's supplied.
407 What if, instead, we did the same kind of thing we did with pairs and v2
408 lists? That is, what if we made the larger computation a "handler" that we
409 passed as an argument to the search?
411         the_search (\search_result. larger_computation search_result other_arguments)
413 What's the advantage of that, you say. Other than to show off how cleverly
414 you can lift.
416 Well, think about it. Think about the difficulty we were having aborting the
417 search. Does this switch-around offer us anything useful?
419 It could.
421 What if the way we implemented the search procedure looked something like this?
423 At a given stage in the search, we wouldn't just apply some function `f` to the
424 head at this stage and the result accumulated so far (from folding the same
425 function, and a base value, to the tail at this stage)...and then pass the result
426 of that application to the embedding, more leftward computation.
428 We'd *instead* give `f` a "handler" that expects the result of the current
429 stage *as an argument*, and then evaluates to what you'd get by passing that
430 result leftwards up the list, as before.
432 Why would we do that, you say? Just more flamboyant lifting?
434 Well, no, there's a real point here. If we give the function a "handler" that
435 encodes the normal continuation of the fold leftwards through the list, we can
436 also give it other "handlers" too. For example, we can also give it the underlined handler:
439         the_search (\search_result. larger_computation search_result other_arguments)
440                            ------------------------------------------------------------------
442 This "handler" encodes the search's having finished, and delivering a final
443 answer to whatever else you wanted your program to do with the result of the
444 search. If you like, at any stage in the search you might just give an argument
445 to *this* handler, instead of giving an argument to the handler that continues
446 the list traversal leftwards. Semantically, this would amount to *aborting* the
447 list traversal! (As we've said before, whether the rest of the list traversal
448 really gets evaluated will depend on what evaluation order is in place. But
449 semantically we'll have avoided it. Our larger computation  won't depend on the
450 rest of the list traversal having been computed.)
452 Do you have the basic idea? Think about how you'd implement it. A good
453 understanding of the v2 lists will give you a helpful model.
455 In broad outline, a single stage of the search would look like before, except
456 now `f` would receive two extra, "handler" arguments. We'll reserve the name `f` for the original fold function, and use `f2` for the function that accepts two additional handler arguments. To get the general idea, you can regard these as interchangeable. If the extra precision might help, then you can pay attention to when we're talking about the handler-taking `f2` or the original `f`. You'll only be *supplying* the `f2` function; the idea will be that the behavior of the original `f` will be implicitly encoded in `f2`'s behavior.
458         f2 3 <sofar value that would have resulted from folding f and z over [2; 1]> <handler to continue folding leftwards> <handler to abort the traversal>
460 `f2`'s job would be to check whether `3` matches the element we're searching for
461 (here also `3`), and if it does, just evaluate to the result of passing `true` to
462 the abort handler. If it doesn't, then evaluate to the result of passing
463 `false` to the continue-leftwards handler.
465 In this case, `f2` wouldn't need to consult the result of folding `f` and `z`
466 over `[2; 1]`, since if we had found the element `3` in more rightward
467 positions of the list, we'd have called the abort handler and this application
468 of `f2` to `3` etc would never be needed. However, in other applications the
469 result of folding `f` and `z` over the more rightward parts of the list would
470 be needed. Consider if you were trying to multiply all the elements of the
471 list, and were going to abort (with the result `0`) if you came across any
472 element in the list that was zero. If you didn't abort, you'd need to know what
473 the more rightward elements of the list multiplied to, because that would
474 affect the answer you passed along to the continue-leftwards handler.
476 A **version 5** list encodes the kind of fold operation we're envisaging here,
477 in the same way that v3 (and [v4](/advanced_lambda/#index1h1)) lists encoded
478 the simpler fold operation. Roughly, the list `[5;4;3;2;1]` would look like
479 this:
482         \f2 z continue_leftwards_handler abort_handler.
483                 <fold f2 and z over [4;3;2;1]>
484                 (\result_of_folding_over_4321. f2 5 result_of_folding_over_4321  continue_leftwards_handler abort_handler)
485                 abort_handler
487         ; or, expanding the fold over [4;3;2;1]:
489         \f2 z continue_leftwards_handler abort_handler.
490                 (\continue_leftwards_handler abort_handler.
491                         <fold f2 and z over [3;2;1]>
492                         (\result_of_folding_over_321. f2 4 result_of_folding_over_321 continue_leftwards_handler abort_handler)
493                         abort_handler
494                 )
495                 (\result_of_folding_over_4321. f2 5 result_of_folding_over_4321  continue_leftwards_handler abort_handler)
496                 abort_handler
498         ; and so on
500 Remarks: the `larger_computation` handler should be supplied as both the
501 `continue_leftwards_handler` and the `abort_handler` for the leftmost
502 application, where the head `5` is supplied to `f2`; because the result of this
503 application should be passed to the larger computation, whether it's a "fall
504 off the left end of the list" result or it's a "I'm finished, possibly early"
505 result. The `larger_computation` handler also then gets passed to the next
506 rightmost stage, where the head `4` is supplied to `f2`, as the `abort_handler` to
507 use if that stage decides it has an early answer.
509 Finally, notice that we're not supplying the application of `f2` to `4` etc as an argument to the application of `f2` to `5` etc---at least, not directly. Instead, we pass
511         (\result_of_folding_over_4321. f2 5 result_of_folding_over_4321 <one_handler> <another_handler>)
513 *to* the application of `f2` to `4` as its "continue" handler. The application of `f2`
514 to `4` can decide whether this handler, or the other, "abort" handler, should be
515 given an argument and constitute its result.
518 I'll say once again: we're using temporally-loaded vocabulary throughout this,
519 but really all we're in a position to mean by that are claims about the result
520 of the complex expression semantically depending only on this, not on that. A
521 demon evaluator who custom-picked the evaluation order to make things maximally
522 bad for you could ensure that all the semantically unnecessary computations got
523 evaluated anyway. We don't yet know any way to prevent that. Later, we'll see
524 ways to *guarantee* one evaluation order rather than another. Of
525 course, in any real computing environment you'll know in advance that you're
526 dealing with a fixed evaluation order and you'll be able to program efficiently
527 around that.
529 In detail, then, here's what our v5 lists will look like:
531         let empty = \f2 z continue_handler abort_handler. continue_handler z  in
532         let make_list = \h t. \f2 z continue_handler abort_handler.
533                 t f2 z (\sofar. f2 h sofar continue_handler abort_handler) abort_handler  in
534         let isempty = \lst larger_computation. lst
535                         ; here's our f2
536                         (\hd sofar continue_handler abort_handler. abort_handler false)
537                         ; here's our z
538                         true
539                         ; here's the continue_handler for the leftmost application of f2
540                         larger_computation
541                         ; here's the abort_handler
542                         larger_computation  in
543         let extract_head = \lst larger_computation. lst
544                         ; here's our f2
545                         (\hd sofar continue_handler abort_handler. continue_handler hd)
546                         ; here's our z
547                         junk
548                         ; here's the continue_handler for the leftmost application of f2
549                         larger_computation
550                         ; here's the abort_handler
551                         larger_computation  in
552         let extract_tail = ; left as exercise
554 These functions are used like this:
556         let my_list = make_list a (make_list b (make_list c empty) in
557         extract_head my_list larger_computation
559 If you just want to see `my_list`'s head, the use `I` as the
560 `larger_computation`.
562 What we've done here does take some work to follow. But it should be within
563 your reach. And once you have followed it, you'll be well on your way to
564 appreciating the full terrible power of continuations.
566 <!-- (Silly [cultural reference](http://www.newgrounds.com/portal/view/33440).) -->
568 Of course, like everything elegant and exciting in this seminar, [Oleg
569 discusses it in much more
570 detail](http://okmij.org/ftp/Streams.html#enumerator-stream).
572 >       *Comments*:
574 >       1.      The technique deployed here, and in the v2 lists, and in our
575 >       implementations of pairs and booleans, is known as
576 >       **continuation-passing style** programming.
578 >       2.      We're still building the list as a right fold, so in a sense the
579 >       application of `f2` to the leftmost element `5` is "outermost". However,
580 >       this "outermost" application is getting lifted, and passed as a *handler*
581 >       to the next right application. Which is in turn getting lifted, and
582 >       passed to its next right application, and so on. So if you
583 >       trace the evaluation of the `extract_head` function to the list `[5;4;3;2;1]`,
584 >       you'll see `1` gets passed as a "this is the head sofar" answer to its
585 >       `continue_handler`; then that answer is discarded and `2` is
586 >       passed as a "this is the head sofar" answer to *its* `continue_handler`,
587 >       and so on. All those steps have to be evaluated to finally get the result
588 >       that `5` is the outer/leftmost head of the list. That's not an efficient way
589 >       to get the leftmost head.
590 >
591 >       We could improve this by building lists as **left folds**. What's that?
592 >
593 >       Well, the right fold of `f` over a list `[a;b;c;d;e]`, using starting value z, is:
594 >
595 >                       f a (f b (f c (f d (f e z))))
596 >
597 >       The left fold on the other hand starts combining `z` with elements from the left. `f z a` is then combined with `b`, and so on:
598 >
599 >                       f (f (f (f (f z a) b) c) d) e
600 >
601 >       or, if we preferred the arguments to each `f` flipped:
602 >
603 >                       f e (f d (f c (f b (f a z))))
604 >
605 >       Recall we implemented v3 lists as their own right-fold functions. We could
606 >       instead implement lists as their own left-fold functions. To do that with our
607 >       v5 lists, we'd replace above:
608 >
609 >                       let make_list = \h t. \f2 z continue_handler abort_handler.
610 >                               f2 h z (\z. t f2 z continue_handler abort_handler) abort_handler
611 >
612 >       Having done that, now `extract_head` can return the leftmost head
613 >       directly, using its `abort_handler`:
614 >
615 >                       let extract_head = \lst larger_computation. lst
616 >                                       (\hd sofar continue_handler abort_handler. abort_handler hd)
617 >                                       junk
618 >                                       larger_computation
619 >                                       larger_computation
620 >
621 >       3.      To extract tails efficiently, too, it'd be nice to fuse the apparatus
622 >       developed in these v5 lists with the ideas from
623 >       [v4](/advanced_lambda/#index1h1) lists. But that is left as an exercise.