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 &equiv; \T. (\x. T (x x)) (\x. T (x x)) Y
31 ~~> (\x. Y (x x)) (\x. Y (x x))
32 ~~> Y ((\x. Y (x x)) (\x. Y (x x)))
33 ~~> Y (Y ((\x. Y (x x)) (\x. Y (x x))))
34 ~~> Y (Y (Y (...(Y (Y Y))...)))</code></pre>
37 #Q: Ouch!  Stop hurting my brain.#
39 A: Is that a question?
41 Let's come at it from the direction of arithmetic.  Recall that we
42 claimed that even `succ`---the function that added one to any
43 number---had a fixed point.  How could there be an X such that X = X+1?
44 That would imply that
46     X <~~> succ X <~~> succ (succ X) <~~> succ (succ (succ X)) <~~> succ (... (succ X)...)
48 In other words, the fixed point of `succ` is a term that is its own
49 successor.  Let's just check that `X = succ X`:
51 <pre><code>let succ = \n s z. s (n s z) in
52 let X = (\x. succ (x x)) (\x. succ (x x)) in
53 succ X
54 &equiv; succ ( (\x. succ (x x)) (\x. succ (x x)) )
55 ~~> succ (succ ( (\x. succ (x x)) (\x. succ (x x)) ))
56 &equiv; succ (succ X)
57 </code></pre>
59 You should see the close similarity with `Y Y` here.
62 #Q. So `Y` applied to `succ` returns a number that is not finite!#
64 A. Yes!  Let's see why it makes sense to think of `Y succ` as a Church
65 numeral:
67 <pre><code>[same definitions]
68 succ X
69 &equiv; (\n s z. s (n s z)) X
70 ~~> \s z. s (X s z)
71 <~~> succ (\s z. s (X s z)) ; using fixed-point reasoning
72 &equiv; (\n s z. s (n s z)) (\s z. s (X s z))
73 ~~> \s z. s ((\s z. s (X s z)) s z)
74 ~~> \s z. s (s (X s z))
75 </code></pre>
77 So `succ X` looks like a numeral: it takes two arguments, `s` and `z`,
78 and returns a sequence of nested applications of `s`...
80 You should be able to prove that `add 2 (Y succ) <~~> Y succ`,
81 likewise for `mul`, `sub`, `pow`.  What happens if we try `sub (Y
82 succ) (Y succ)`?  What would you expect infinity minus infinity to be?
83 (Hint: choose your evaluation strategy so that you add two `s`s to the
84 first number for every `s` that you add to the second number.)
86 This is amazing, by the way: we're proving things about a term that
87 represents arithmetic infinity.
89 It's important to bear in mind the simplest term in question is not
90 infinite:
92         Y succ = (\f. (\x. f (x x)) (\x. f (x x))) (\n s z. s (n s z))
94 The way that infinity enters into the picture is that this term has
95 no normal form: no matter how many times we perform beta reduction,
96 there will always be an opportunity for more beta reduction.  (Lather,
97 rinse, repeat!)
100 #Q. That reminds me, what about [[evaluation order]]?#
102 A. For a recursive function that has a well-behaved base case, such as
103 the factorial function, evaluation order is crucial.  In the following
104 computation, we will arrive at a normal form.  Watch for the moment at
105 which we have to make a choice about which beta reduction to perform
106 next: one choice leads to a normal form, the other choice leads to
107 endless reduction:
109 <pre><code>let prefact = \f n. iszero n 1 (mul n (f (pred n))) in
110 let fact = Y prefact in
111 fact 2
112 &equiv; [(\f. (\x. f (x x)) (\x. f (x x))) prefact] 2
113 ~~> [(\x. prefact (x x)) (\x. prefact (x x))] 2
114 ~~> [prefact ((\x. prefact (x x)) (\x. prefact (x x)))] 2
115 ~~> [prefact (prefact ((\x. prefact (x x)) (\x. prefact (x x))))] 2
116 &equiv; [ (\f n. iszero n 1 (mul n (f (pred n)))) (prefact ((\x. prefact (x x)) (\x. prefact (x x))))] 2
117 ~~> [\n. iszero n 1 (mul n ([prefact ((\x. prefact (x x)) (\x. prefact (x x)))] (pred n)))] 2
118 ~~> iszero 2 1 (mul 2 ([prefact ((\x. prefact (x x)) (\x. prefact (x x)))] (pred 2)))
119 ~~> mul 2 ([prefact ((\x. prefact (x x)) (\x. prefact (x x)))] 1)
120 ...
121 ~~> mul 2 (mul 1 ([prefact ((\x. prefact (x x)) (\x. prefact (x x)))] 0))
122 &equiv; mul 2 (mul 1 (iszero 0 1 ([prefact ((\x. prefact (x x)) (\x. prefact (x x)))] (pred 0))))
123 ~~> mul 2 (mul 1 1)
124 ~~> mul 2 1
125 ~~> 2
126 </code></pre>
128 The crucial step is the third from the last.  We have our choice of
129 either evaluating the test `iszero 0 1 ...`, which evaluates to `1`,
130 no matter what the ... contains;
131 or we can evaluate the `Y` pump, `(\x. prefact (x x)) (\x. prefact (x x))`, to
132 produce another copy of `prefact`.  If we postpone evaluting the
133 `iszero` test, we'll pump out copy after copy of `prefact`, and never
134 realize that we've bottomed out in the recursion.  But if we adopt a
135 leftmost/call-by-name/normal-order evaluation strategy, we'll always
136 start with the `iszero` predicate, and only produce a fresh copy of
137 `prefact` if we are forced to.
140 #Q.  You claimed that the Ackerman 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 Ackerman function using full recursion.#
143 A. OK:
145         A(m,n) =
146                 | when m == 0 -> n + 1
147                 | else when n == 0 -> A(m-1,1)
148                 | else -> A(m-1, A(m,n-1))
150         let A = Y (\A m n. iszero m (succ n) (iszero n (A (pred m) 1) (A (pred m) (A m (pred n)))))
152 So for instance:
154         A 1 2
155         ~~> A 0 (A 1 1)
156         ~~> A 0 (A 0 (A 1 0))
157         ~~> A 0 (A 0 (A 0 1))
158         ~~> A 0 (A 0 2)
159         ~~> A 0 3
160         ~~> 4
162 `A 1 x` is to `A 0 x` as addition is to the successor function;
163 `A 2 x` is to `A 1 x` as multiplication is to addition;
164 `A 3 x` is to `A 2 x` as exponentiation is to multiplication---
165 so `A 4 x` is to `A 3 x` as hyper-exponentiation is to exponentiation...
167 #Q. What other questions should I be asking?#
169 *    What is it about the variant fixed-point combinators that makes
170      them compatible with a call-by-value evaluation strategy?
172 *    How do you know that the Ackerman function can't be computed
173      using primitive recursion techniques?
175 *    What *exactly* is primitive recursion?
177 *    I hear that `Y` delivers the *least* fixed point.  Least
178      according to what ordering?  How do you know it's least?
179      Is leastness important?
183 #Sets#
185 You're now already in a position to implement sets: that is, collections with
186 no intrinsic order where elements can occur at most once. Like lists, we'll
187 understand the basic set structures to be *type-homogenous*. So you might have
188 a set of integers, or you might have a set of pairs of integers, but you
189 wouldn't have a set that mixed both types of elements. Something *like* the
190 last option is also achievable, but it's more difficult, and we won't pursue it
191 now. In fact, we won't talk about sets of pairs, either. We'll just talk about
192 sets of integers. The same techniques we discuss here could also be applied to
193 sets of pairs of integers, or sets of triples of booleans, or sets of pairs
194 whose first elements are booleans, and whose second elements are triples of
195 integers. And so on.
197 (You're also now in a position to implement *multi*sets: that is, collections
198 with no intrinsic order where elements can occur multiple times: the multiset
199 {a,a} is distinct from the multiset {a}. But we'll leave these as an exercise.)
201 The easiest way to implement sets of integers would just be to use lists. When
202 you "add" a member to a set, you'd get back a list that was either identical to
203 the original list, if the added member already was present in it, or consisted
204 of a new list with the added member prepended to the old list. That is:
206         let empty_set = empty  in
207         ; see the library for definitions of any and eq
208         let make_set = \new_member old_set. any (eq new_member) old_set
209                                                 ; if any element in old_set was eq new_member
210                                                 old_set
211                                                 ; else
212                                                 make_list new_member old_set
214 Think about how you'd implement operations like `set_union`,
215 `set_intersection`, and `set_difference` with this implementation of sets.
217 The implementation just described works, and it's the simplest to code.
218 However, it's pretty inefficient. If you had a 100-member set, and you wanted
219 to create a set which had all those 100-members and some possibly new element
220 `e`, you might need to check all 100 members to see if they're equal to `e`
221 before concluding they're not, and returning the new list. And comparing for
222 numeric equality is a moderately expensive operation, in the first place.
224 (You might say, well, what's the harm in just prepending `e` to the list even
225 if it already occurs later in the list. The answer is, if you don't keep track
226 of things like this, it will likely mess up your implementations of
227 `set_difference` and so on. You'll have to do the book-keeping for duplicates
228 at some point in your code. It goes much more smoothly if you plan this from
229 the very beginning.)
231 How might we make the implementation more efficient? Well, the *semantics* of
232 sets says that they have no intrinsic order. That means, there's no difference
233 between the set {a,b} and the set {b,a}; whereas there is a difference between
234 the *list* `[a;b]` and the list `[b;a]`. But this semantic point can be respected
235 even if we *implement* sets with something ordered, like list---as we're
236 already doing. And we might *exploit* the intrinsic order of lists to make our
237 implementation of sets more efficient.
239 What we could do is arrange it so that a list that implements a set always
240 keeps in elements in some specified order. To do this, there'd have *to be*
241 some way to order its elements. Since we're talking now about sets of numbers,
242 that's easy. (If we were talking about sets of pairs of numbers, we'd use
243 "lexicographic" ordering, where `(a,b) < (c,d)` iff `a < c or (a == c and b <
244 d)`.)
246 So, if we were searching the list that implements some set to see if the number
247 `5` belonged to it, once we get to elements in the list that are larger than `5`,
248 we can stop. If we haven't found `5` already, we know it's not in the rest of the
249 list either.
251 This is an improvement, but it's still a "linear" search through the list.
252 There are even more efficient methods, which employ "binary" searching. They'd
253 represent the set in such a way that you could quickly determine whether some
254 element fell in one half, call it the left half, of the structure that
255 implements the set, if it belonged to the set at all. Or that it fell in the
256 right half, it it belonged to the set at all. And then the same sort of
257 determination could be made for whichever half you were directed to. And then
258 for whichever quarter you were directed to next. And so on. Until you either
259 found the element or exhausted the structure and could then conclude that the
260 element in question was not part of the set. These sorts of structures are done
261 using **binary trees** (see below).
264 #Aborting a search through a list#
266 We said that the sorted-list implementation of a set was more efficient than
267 the unsorted-list implementation, because as you were searching through the
268 list, you could come to a point where you knew the element wasn't going to be
269 found. So you wouldn't have to continue the search.
271 If your implementation of lists was, say v1 lists plus the Y-combinator, then
272 this is exactly right. When you get to a point where you know the answer, you
273 can just deliver that answer, and not branch into any further recursion. If
274 you've got the right evaluation strategy in place, everything will work out
275 fine.
277 But what if you're using v3 lists? What options would you have then for
278 aborting a search?
280 Well, suppose we're searching through the list `[5;4;3;2;1]` to see if it
281 contains the number `3`. The expression which represents this search would have
282 something like the following form:
284         ..................<eq? 1 3>  ~~>
285         .................. false     ~~>
286         .............<eq? 2 3>       ~~>
287         ............. false          ~~>
288         .........<eq? 3 3>           ~~>
289         ......... true               ~~>
290         ?
292 Of course, whether those reductions actually followed in that order would
293 depend on what reduction strategy was in place. But the result of folding the
294 search function over the part of the list whose head is `3` and whose tail is `[2;
295 1]` will *semantically* depend on the result of applying that function to the
296 more rightmost pieces of the list, too, regardless of what order the reduction
297 is computed by. Conceptually, it will be easiest if we think of the reduction
298 happening in the order displayed above.
300 Well, once we've found a match between our sought number `3` and some member of
301 the list, we'd like to avoid any further unnecessary computations and just
302 deliver the answer `true` as "quickly" or directly as possible to the larger
303 computation in which the search was embedded.
305 With a Y-combinator based search, as we said, we could do this by just not
306 following a recursion branch.
308 But with the v3 lists, the fold is "pre-programmed" to continue over the whole
309 list. There is no way for us to bail out of applying the search function to the
310 parts of the list that have head `4` and head `5`, too.
312 We *can* avoid *some* unneccessary computation. The search function can detect
313 that the result we've accumulated so far during the fold is now `true`, so we
314 don't need to bother comparing `4` or `5` to `3` for equality. That will simplify the
315 computation to some degree, since as we said, numerical comparison in the
316 system we're working in is moderately expensive.
318 However, we're still going to have to traverse the remainder of the list. That
319 `true` result will have to be passed along all the way to the leftmost head of
320 the list. Only then can we deliver it to the larger computation in which the
321 search was embedded.
323 It would be better if there were some way to "abort" the list traversal. If,
324 having found the element we're looking for (or having determined that the
325 element isn't going to be found), we could just immediately stop traversing the
326 list with our answer. **Continuations** will turn out to let us do that.
328 We won't try yet to fully exploit the terrible power of continuations. But
329 there's a way that we can gain their benefits here locally, without yet having
330 a fully general machinery or understanding of what's going on.
332 The key is to recall how our implementations of booleans and pairs worked.
333 Remember that with pairs, we supply the pair "handler" to the pair as *an
334 argument*, rather than the other way around:
336         pair (\x y. add x y)
338 or:
340         pair (\x y. x)
342 to get the first element of the pair. Of course you can lift that if you want:
344 <pre><code>extract_fst &equiv; \pair. pair (\x y. x)</code></pre>
346 but at a lower level, the pair is still accepting its handler as an argument,
347 rather than the handler taking the pair as an argument. (The handler gets *the
348 pair's elements*, not the pair itself, as arguments.)
350 >       *Terminology*: we'll try to use names of the form `get_foo` for handlers, and
351 names of the form `extract_foo` for lifted versions of them, that accept the
352 lists (or whatever data structure we're working with) as arguments. But we may
353 sometimes forget.
355 The v2 implementation of lists followed a similar strategy:
357         v2list (\h t. do_something_with_h_and_t) result_if_empty
359 If the `v2list` here is not empty, then this will reduce to the result of
360 supplying the list's head and tail to the handler `(\h t.
361 do_something_with_h_and_t)`.
363 Now, what we've been imagining ourselves doing with the search through the v3
364 list is something like this:
367         larger_computation (search_through_the_list_for_3) other_arguments
369 That is, the result of our search is supplied as an argument (perhaps together
370 with other arguments) to the "larger computation". Without knowing the
371 evaluation order/reduction strategy, we can't say whether the search is
372 evaluated before or after it's substituted into the larger computation. But
373 semantically, the search is the argument and the larger computation is the
374 function to which it's supplied.
376 What if, instead, we did the same kind of thing we did with pairs and v2
377 lists? That is, what if we made the larger computation a "handler" that we
378 passed as an argument to the search?
380         the_search (\search_result. larger_computation search_result other_arguments)
382 What's the advantage of that, you say. Other than to show off how cleverly
383 you can lift.
385 Well, think about it. Think about the difficulty we were having aborting the
386 search. Does this switch-around offer us anything useful?
388 It could.
390 What if the way we implemented the search procedure looked something like this?
392 At a given stage in the search, we wouldn't just apply some function `f` to the
393 head at this stage and the result accumulated so far (from folding the same
394 function, and a base value, to the tail at this stage)...and then pass the result
395 of that application to the embedding, more leftward computation.
397 We'd *instead* give `f` a "handler" that expects the result of the current
398 stage *as an argument*, and then evaluates to what you'd get by passing that
399 result leftwards up the list, as before.
401 Why would we do that, you say? Just more flamboyant lifting?
403 Well, no, there's a real point here. If we give the function a "handler" that
404 encodes the normal continuation of the fold leftwards through the list, we can
405 also give it other "handlers" too. For example, we can also give it the underlined handler:
408         the_search (\search_result. larger_computation search_result other_arguments)
409                            ------------------------------------------------------------------
411 This "handler" encodes the search's having finished, and delivering a final
412 answer to whatever else you wanted your program to do with the result of the
413 search. If you like, at any stage in the search you might just give an argument
414 to *this* handler, instead of giving an argument to the handler that continues
415 the list traversal leftwards. Semantically, this would amount to *aborting* the
416 list traversal! (As we've said before, whether the rest of the list traversal
417 really gets evaluated will depend on what evaluation order is in place. But
418 semantically we'll have avoided it. Our larger computation  won't depend on the
419 rest of the list traversal having been computed.)
421 Do you have the basic idea? Think about how you'd implement it. A good
422 understanding of the v2 lists will give you a helpful model.
424 In broad outline, a single stage of the search would look like before, except
425 now f would receive two extra, "handler" arguments.
427         f 3 <result of folding f and z over [2; 1]> <handler to continue folding leftwards> <handler to abort the traversal>
429 `f`'s job would be to check whether `3` matches the element we're searching for
430 (here also `3`), and if it does, just evaluate to the result of passing `true` to
431 the abort handler. If it doesn't, then evaluate to the result of passing
432 `false` to the continue-leftwards handler.
434 In this case, `f` wouldn't need to consult the result of folding `f` and `z` over `[2;
435 1]`, since if we had found the element `3` in more rightward positions of the
436 list, we'd have called the abort handler and this application of `f` to `3` etc
437 would never be needed. However, in other applications the result of folding `f`
438 and `z` over the more rightward parts of the list would be needed. Consider if
439 you were trying to multiply all the elements of the list, and were going to
440 abort (with the result `0`) if you came across any element in the list that was
441 zero. If you didn't abort, you'd need to know what the more rightward elements
442 of the list multiplied to, because that would affect the answer you passed
443 along to the continue-leftwards handler.
445 A **version 5** list encodes the kind of fold operation we're envisaging here, in
446 the same way that v3 (and [v4](/advanced/#index1h1)) lists encoded the simpler fold operation.
447 Roughly, the list `[5;4;3;2;1]` would look like this:
450         \f z continue_leftwards_handler abort_handler.
451                 <fold f and z over [4;3;2;1]>
452                 (\result_of_fold_over_4321. f 5 result_of_fold_over_4321  continue_leftwards_handler abort_handler)
453                 abort_handler
455         ; or, expanding the fold over [4;3;2;1]:
457         \f z continue_leftwards_handler abort_handler.
458                 (\continue_leftwards_handler abort_handler.
459                         <fold f and z over [3;2;1]>
460                         (\result_of_fold_over_321. f 4 result_of_fold_over_321 continue_leftwards_handler abort_handler)
461                         abort_handler
462                 )
463                 (\result_of_fold_over_4321. f 5 result_of_fold_over_4321  continue_leftwards_handler abort_handler)
464                 abort_handler
466         ; and so on
468 Remarks: the `larger_computation` handler should be supplied as both the
469 `continue_leftwards_handler` and the `abort_handler` for the leftmost
470 application, where the head `5` is supplied to `f`; because the result of this
471 application should be passed to the larger computation, whether it's a "fall
472 off the left end of the list" result or it's a "I'm finished, possibly early"
473 result. The `larger_computation` handler also then gets passed to the next
474 rightmost stage, where the head `4` is supplied to `f`, as the `abort_handler` to
475 use if that stage decides it has an early answer.
477 Finally, notice that we don't have the result of applying `f` to `4` etc given as
478 an argument to the application of `f` to `5` etc. Instead, we pass
480         (\result_of_fold_over_4321. f 5 result_of_fold_over_4321 <one_handler> <another_handler>)
482 *to* the application of `f` to `4` as its "continue" handler. The application of `f`
483 to `4` can decide whether this handler, or the other, "abort" handler, should be
484 given an argument and constitute its result.
487 I'll say once again: we're using temporally-loaded vocabulary throughout this,
488 but really all we're in a position to mean by that are claims about the result
489 of the complex expression semantically depending only on this, not on that. A
490 demon evaluator who custom-picked the evaluation order to make things maximally
491 bad for you could ensure that all the semantically unnecessary computations got
492 evaluated anyway. We don't have any way to prevent that. Later,
493 we'll see ways to *semantically guarantee* one evaluation order rather than
494 another. Though even then the demonic evaluation-order-chooser could make it
495 take unnecessarily long to compute the semantically guaranteed result. Of
496 course, in any real computing environment you'll know you're dealing with a
497 fixed evaluation order and you'll be able to program efficiently around that.
499 In detail, then, here's what our v5 lists will look like:
501         let empty = \f z continue_handler abort_handler. continue_handler z  in
502         let make_list = \h t. \f z continue_handler abort_handler.
503                 t f z (\sofar. f h sofar continue_handler abort_handler) abort_handler  in
504         let isempty = \lst larger_computation. lst
505                         ; here's our f
506                         (\hd sofar continue_handler abort_handler. abort_handler false)
507                         ; here's our z
508                         true
509                         ; here's the continue_handler for the leftmost application of f
510                         larger_computation
511                         ; here's the abort_handler
512                         larger_computation  in
513         let extract_head = \lst larger_computation. lst
514                         ; here's our f
515                         (\hd sofar continue_handler abort_handler. continue_handler hd)
516                         ; here's our z
517                         junk
518                         ; here's the continue_handler for the leftmost application of f
519                         larger_computation
520                         ; here's the abort_handler
521                         larger_computation  in
522         let extract_tail = ; left as exercise
524 These functions are used like this:
526         let my_list = make_list a (make_list b (make_list c empty) in
529 If you just want to see `my_list`'s head, the use `I` as the
530 `larger_computation`.
532 What we've done here does take some work to follow. But it should be within
533 your reach. And once you have followed it, you'll be well on your way to
534 appreciating the full terrible power of continuations.
536 <!-- (Silly [cultural reference](http://www.newgrounds.com/portal/view/33440).) -->
538 Of course, like everything elegant and exciting in this seminar, [Oleg
539 discusses it in much more
540 detail](http://okmij.org/ftp/Streams.html#enumerator-stream).
544 1.      The technique deployed here, and in the v2 lists, and in our implementations
545         of pairs and booleans, is known as **continuation-passing style** programming.
547 2.      We're still building the list as a right fold, so in a sense the
548         application of `f` to the leftmost element `5` is "outermost". However,
549         this "outermost" application is getting lifted, and passed as a *handler*
550         to the next right application. Which is in turn getting lifted, and
551         passed to its next right application, and so on. So if you
552         trace the evaluation of the `extract_head` function to the list `[5;4;3;2;1]`,
553         you'll see `1` gets passed as a "this is the head sofar" answer to its
555         passed as a "this is the head sofar" answer to *its* `continue_handler`,
556         and so on. All those steps have to be evaluated to finally get the result
557         that `5` is the outer/leftmost head of the list. That's not an efficient way
558         to get the leftmost head.
560         We could improve this by building lists as left folds when implementing them
561         as continuation-passing style folds. We'd just replace above:
563                 let make_list = \h t. \f z continue_handler abort_handler.
564                         f h z (\z. t f z continue_handler abort_handler) abort_handler
566         now `extract_head` should return the leftmost head directly, using its `abort_handler`:
568                 let extract_head = \lst larger_computation. lst
569                                 (\hd sofar continue_handler abort_handler. abort_handler hd)
570                                 junk
571                                 larger_computation
572                                 larger_computation
574 3.      To extract tails efficiently, too, it'd be nice to fuse the apparatus developed
575         in these v5 lists with the ideas from [v4](/advanced/#index1h1) lists.
576         But that also is left as an exercise.