tweak advanced
[lambda.git] / miscellaneous_lambda_challenges_and_advanced_topics.mdwn
index 6d78f0b..ea7799c 100644 (file)
@@ -17,12 +17,12 @@ can use.
        native `let rec` or `define`, then you can't use the fixed-point combinators
        `Y` or <code>&Theta;</code>. Expressions using them will have non-terminating
        reductions, with Scheme's eager/call-by-value strategy. There are other
        native `let rec` or `define`, then you can't use the fixed-point combinators
        `Y` or <code>&Theta;</code>. Expressions using them will have non-terminating
        reductions, with Scheme's eager/call-by-value strategy. There are other
-       fixed-point combinators you can use with Scheme (in the [[week 3]] notes they
+       fixed-point combinators you can use with Scheme (in the [week 3 notes](/week3/#index7h2) they
        were <code>Y&prime;</code> and <code>&Theta;&prime;</code>. But even with
        were <code>Y&prime;</code> and <code>&Theta;&prime;</code>. But even with
-       those evaluation order still matters: for some (admittedly unusual)
+       them, evaluation order still matters: for some (admittedly unusual)
        evaluation strategies, expressions using them will also be non-terminating.
 
        evaluation strategies, expressions using them will also be non-terminating.
 
-       The fixed-point combinators may be the conceptual heros. They are cool and
+       The fixed-point combinators may be the conceptual stars. They are cool and
        mathematically elegant. But for efficiency and implementation elegance, it's
        best to know how to do as much as you can without them. (Also, that knowledge
        could carry over to settings where the fixed point combinators are in
        mathematically elegant. But for efficiency and implementation elegance, it's
        best to know how to do as much as you can without them. (Also, that knowledge
        could carry over to settings where the fixed point combinators are in
@@ -30,10 +30,10 @@ can use.
 
        This is why the v3 lists and numbers are so lovely. However, one disadvantage
        to them is that it's relatively inefficient to extract a list's tail, or get a
 
        This is why the v3 lists and numbers are so lovely. However, one disadvantage
        to them is that it's relatively inefficient to extract a list's tail, or get a
-       number's predecessor. To get the tail of the list [a;b;c;d;e], one will
+       number's predecessor. To get the tail of the list `[a;b;c;d;e]`, one will
        basically be performing some operation that builds up the tail afresh: at
        basically be performing some operation that builds up the tail afresh: at
-       different stages, one will have built up [e], then [d;e], then [c;d;e], and
-       finally [b;c;d;e]. With short lists, this is no problem, but with longer lists
+       different stages, one will have built up `[e]`, then `[d;e]`, then `[c;d;e]`, and
+       finally `[b;c;d;e]`. With short lists, this is no problem, but with longer lists
        it takes longer and longer. And it may waste more of your computer's memory
        than you'd like. Similarly for obtaining a number's predecessor.
 
        it takes longer and longer. And it may waste more of your computer's memory
        than you'd like. Similarly for obtaining a number's predecessor.
 
@@ -44,7 +44,7 @@ can use.
 
        A clever approach would marry these two strategies.
 
 
        A clever approach would marry these two strategies.
 
-       Version 3 makes the list [a; b; c; d; e] look like this:
+       Version 3 makes the list `[a;b;c;d;e]` look like this:
 
                \f z. f a (f b (f c (f d (f e z))))
 
 
                \f z. f a (f b (f c (f d (f e z))))
 
@@ -56,22 +56,22 @@ can use.
 
                \f z. f a <the tail itself> <the result of folding f and z over the tail>
 
 
                \f z. f a <the tail itself> <the result of folding f and z over the tail>
 
-       That is, now f is a function expecting *three* arguments: the head of the
+       That is, now `f` is a function expecting *three* arguments: the head of the
        current list, the tail of the current list, and the result of continuing to
        current list, the tail of the current list, and the result of continuing to
-       fold f over the tail, with a given base value z.
+       fold `f` over the tail, with a given base value `z`.
 
 
-       Call this a **version 4** list. The empty list could be the same:
+       Call this a **version 4** list. The empty list can be the same as in v3:
 
 
-               empty === \f z. z
+       <pre><code>empty &equiv; \f z. z</code></pre>
 
        The list constructor would be:
 
 
        The list constructor would be:
 
-               make_list === \h t. \f z. f h t (t f z)
+       <pre><code>make_list &equiv; \h t. \f z. f h t (t f z)</code></pre>
 
        It differs from the version 3 `make_list` only in adding the extra argument
        `t` to the new, outer application of `f`.
 
 
        It differs from the version 3 `make_list` only in adding the extra argument
        `t` to the new, outer application of `f`.
 
-       Similarly, 5 as a v3 or Church numeral looks like this:
+       Similarly, `five` as a v3 or Church numeral looks like this:
 
                \s z. s (s (s (s (s z))))
 
 
                \s z. s (s (s (s (s z))))
 
@@ -83,8 +83,8 @@ can use.
 
                \s z. s <pred 5> <the result of applying s to z (pred 5)-many times>
 
 
                \s z. s <pred 5> <the result of applying s to z (pred 5)-many times>
 
-       That is, now s is a function expecting *two* arguments: the predecessor of the
-       current number, and the result of continuing to apply s to the base value z
+       That is, now `s` is a function expecting *two* arguments: the predecessor of the
+       current number, and the result of continuing to apply `s` to the base value `z`
        predecessor-many times.
 
        Jim had the pleasure of "inventing" these implementations himself. However,
        predecessor-many times.
 
        Jim had the pleasure of "inventing" these implementations himself. However,
@@ -117,7 +117,7 @@ can use.
        of a new list with the added member prepended to the old list. That is:
 
                let empty_set = empty  in
        of a new list with the added member prepended to the old list. That is:
 
                let empty_set = empty  in
-               ; see the library for definition of any
+               ; see the library for definitions of any and eq
                let make_set = \new_member old_set. any (eq new_member) old_set
                                                        ; if any element in old_set was eq new_member
                                                        old_set
                let make_set = \new_member old_set. any (eq new_member) old_set
                                                        ; if any element in old_set was eq new_member
                                                        old_set
@@ -144,7 +144,7 @@ can use.
        How might we make the implementation more efficient? Well, the *semantics* of
        sets says that they have no intrinsic order. That means, there's no difference
        between the set {a,b} and the set {b,a}; whereas there is a difference between
        How might we make the implementation more efficient? Well, the *semantics* of
        sets says that they have no intrinsic order. That means, there's no difference
        between the set {a,b} and the set {b,a}; whereas there is a difference between
-       the *list* [a;b] and the list [b;a]. But this semantic point can be respected
+       the *list* `[a;b]` and the list `[b;a]`. But this semantic point can be respected
        even if we *implement* sets with something ordered, like list---as we're
        already doing. And we might *exploit* the intrinsic order of lists to make our
        implementation of sets more efficient.
        even if we *implement* sets with something ordered, like list---as we're
        already doing. And we might *exploit* the intrinsic order of lists to make our
        implementation of sets more efficient.
@@ -157,8 +157,8 @@ can use.
        d)`.)
 
        So, if we were searching the list that implements some set to see if the number
        d)`.)
 
        So, if we were searching the list that implements some set to see if the number
-       5 belonged to it, once we get to elements in the list that are larger than 5,
-       we can stop. If we haven't found 5 already, we know it's not in the rest of the
+       `5` belonged to it, once we get to elements in the list that are larger than `5`,
+       we can stop. If we haven't found `5` already, we know it's not in the rest of the
        list either.
 
        This is an improvement, but it's still a "linear" search through the list.
        list either.
 
        This is an improvement, but it's still a "linear" search through the list.
@@ -190,8 +190,8 @@ can use.
        But what if you're using v3 lists? What options would you have then for
        aborting a search?
 
        But what if you're using v3 lists? What options would you have then for
        aborting a search?
 
-       Well, suppose we're searching through the list [5; 4; 3; 2; 1] to see if it
-       contains the number 3. The expression which represents this search would have
+       Well, suppose we're searching through the list `[5;4;3;2;1]` to see if it
+       contains the number `3`. The expression which represents this search would have
        something like the following form:
 
                ..................<eq? 1 3>  ~~>
        something like the following form:
 
                ..................<eq? 1 3>  ~~>
@@ -204,13 +204,13 @@ can use.
 
        Of course, whether those reductions actually followed in that order would
        depend on what reduction strategy was in place. But the result of folding the
 
        Of course, whether those reductions actually followed in that order would
        depend on what reduction strategy was in place. But the result of folding the
-       search function over the part of the list whose head is 3 and whose tail is [2;
-       1] will *semantically* depend on the result of applying that function to the
+       search function over the part of the list whose head is `3` and whose tail is `[2;
+       1]` will *semantically* depend on the result of applying that function to the
        more rightmost pieces of the list, too, regardless of what order the reduction
        is computed by. Conceptually, it will be easiest if we think of the reduction
        happening in the order displayed above.
 
        more rightmost pieces of the list, too, regardless of what order the reduction
        is computed by. Conceptually, it will be easiest if we think of the reduction
        happening in the order displayed above.
 
-       Well, once we've found a match between our sought number 3 and some member of
+       Well, once we've found a match between our sought number `3` and some member of
        the list, we'd like to avoid any further unnecessary computations and just
        deliver the answer `true` as "quickly" or directly as possible to the larger
        computation in which the search was embedded.
        the list, we'd like to avoid any further unnecessary computations and just
        deliver the answer `true` as "quickly" or directly as possible to the larger
        computation in which the search was embedded.
@@ -220,11 +220,11 @@ can use.
 
        But with the v3 lists, the fold is "pre-programmed" to continue over the whole
        list. There is no way for us to bail out of applying the search function to the
 
        But with the v3 lists, the fold is "pre-programmed" to continue over the whole
        list. There is no way for us to bail out of applying the search function to the
-       parts of the list that have head 4 and head 5, too.
+       parts of the list that have head `4` and head `5`, too.
 
 
-       We *can* avoid some unneccessary computation. The search function can detect
-       that the result we've accumulated so far during the fold is now true, so we
-       don't need to bother comparing 4 or 5 to 3 for equality. That will simplify the
+       We *can* avoid *some* unneccessary computation. The search function can detect
+       that the result we've accumulated so far during the fold is now `true`, so we
+       don't need to bother comparing `4` or `5` to `3` for equality. That will simplify the
        computation to some degree, since as we said, numerical comparison in the
        system we're working in is moderately expensive.
 
        computation to some degree, since as we said, numerical comparison in the
        system we're working in is moderately expensive.
 
@@ -236,7 +236,7 @@ can use.
        It would be better if there were some way to "abort" the list traversal. If,
        having found the element we're looking for (or having determined that the
        element isn't going to be found), we could just immediately stop traversing the
        It would be better if there were some way to "abort" the list traversal. If,
        having found the element we're looking for (or having determined that the
        element isn't going to be found), we could just immediately stop traversing the
-       list with our answer. Continuations will turn out to let us do that.
+       list with our answer. **Continuations** will turn out to let us do that.
 
        We won't try yet to fully exploit the terrible power of continuations. But
        there's a way that we can gain their benefits here locally, without yet having
 
        We won't try yet to fully exploit the terrible power of continuations. But
        there's a way that we can gain their benefits here locally, without yet having
@@ -254,17 +254,22 @@ can use.
 
        to get the first element of the pair. Of course you can lift that if you want:
 
 
        to get the first element of the pair. Of course you can lift that if you want:
 
-               extract_1st === \pair. pair (\x y. x)
+       <pre><code>extract_fst &equiv; \pair. pair (\x y. x)</code></pre>
 
        but at a lower level, the pair is still accepting its handler as an argument,
        rather than the handler taking the pair as an argument. (The handler gets *the
        pair's elements*, not the pair itself, as arguments.)
 
 
        but at a lower level, the pair is still accepting its handler as an argument,
        rather than the handler taking the pair as an argument. (The handler gets *the
        pair's elements*, not the pair itself, as arguments.)
 
+       >       *Terminology*: we'll try to use names of the form `get_foo` for handlers, and
+       names of the form `extract_foo` for lifted versions of them, that accept the
+       lists (or whatever data structure we're working with) as arguments. But we may
+       sometimes forget.
+
        The v2 implementation of lists followed a similar strategy:
 
                v2list (\h t. do_something_with_h_and_t) result_if_empty
 
        The v2 implementation of lists followed a similar strategy:
 
                v2list (\h t. do_something_with_h_and_t) result_if_empty
 
-       If the v2list here is not empty, then this will reduce to the result of
+       If the `v2list` here is not empty, then this will reduce to the result of
        supplying the list's head and tail to the handler `(\h t.
        do_something_with_h_and_t)`.
 
        supplying the list's head and tail to the handler `(\h t.
        do_something_with_h_and_t)`.
 
@@ -281,11 +286,14 @@ can use.
        semantically, the search is the argument and the larger computation is the
        function to which it's supplied.
 
        semantically, the search is the argument and the larger computation is the
        function to which it's supplied.
 
-       What if, instead, we did the same kind of thing we did with pairs and v2 lists? That is, what if we made the larger computation a "handler" that we passed as an argument to the search?
+       What if, instead, we did the same kind of thing we did with pairs and v2
+       lists? That is, what if we made the larger computation a "handler" that we
+       passed as an argument to the search?
 
                the_search (\search_result. larger_computation search_result other_arguments)
 
 
                the_search (\search_result. larger_computation search_result other_arguments)
 
-       What's the advantage of that, you say. Other than to show off how cleverly you can lift.
+       What's the advantage of that, you say. Other than to show off how cleverly
+       you can lift.
 
        Well, think about it. Think about the difficulty we were having aborting the
        search. Does this switch-around offer us anything useful?
 
        Well, think about it. Think about the difficulty we were having aborting the
        search. Does this switch-around offer us anything useful?
@@ -294,20 +302,20 @@ can use.
 
        What if the way we implemented the search procedure looked something like this?
 
 
        What if the way we implemented the search procedure looked something like this?
 
-       At a given stage in the search, we wouldn't just apply some function f to the
-       head at this stage and the result accumulated so farfrom folding the same
-       function (and a base value) to the tail at this stage. And then pass the result
-       of doing so leftward along the rest of the list.
+       At a given stage in the search, we wouldn't just apply some function `f` to the
+       head at this stage and the result accumulated so far (from folding the same
+       function, and a base value, to the tail at this stage)...and then pass the result
+       of that application to the embedding, more leftward computation.
 
 
-       We'd also give that function a "handler" that expected the result of the
-       current stage as an argument, and evaluated to passing that result leftwards
-       along the rest of the list.
+       We'd *instead* give `f` a "handler" that expects the result of the current
+       stage *as an argument*, and then evaluates to what you'd get by passing that
+       result leftwards up the list, as before. 
 
        Why would we do that, you say? Just more flamboyant lifting?
 
        Well, no, there's a real point here. If we give the function a "handler" that
 
        Why would we do that, you say? Just more flamboyant lifting?
 
        Well, no, there's a real point here. If we give the function a "handler" that
-       encodes the normal continuation of the fold leftwards through the list. We can
-       give it another "handler" as well. We can also give it the underlined handler:
+       encodes the normal continuation of the fold leftwards through the list, we can
+       also give it other "handlers" too. For example, we can also give it the underlined handler:
 
 
                the_search (\search_result. larger_computation search_result other_arguments)
 
 
                the_search (\search_result. larger_computation search_result other_arguments)
@@ -316,7 +324,7 @@ can use.
        This "handler" encodes the search's having finished, and delivering a final
        answer to whatever else you wanted your program to do with the result of the
        search. If you like, at any stage in the search you might just give an argument
        This "handler" encodes the search's having finished, and delivering a final
        answer to whatever else you wanted your program to do with the result of the
        search. If you like, at any stage in the search you might just give an argument
-       to this handler, instead of giving an argument to the handler that continues
+       to *this* handler, instead of giving an argument to the handler that continues
        the list traversal leftwards. Semantically, this would amount to *aborting* the
        list traversal! (As we've said before, whether the rest of the list traversal
        really gets evaluated will depend on what evaluation order is in place. But
        the list traversal leftwards. Semantically, this would amount to *aborting* the
        list traversal! (As we've said before, whether the rest of the list traversal
        really gets evaluated will depend on what evaluation order is in place. But
@@ -331,60 +339,61 @@ can use.
 
                f 3 <result of folding f and z over [2; 1]> <handler to continue folding leftwards> <handler to abort the traversal>
 
 
                f 3 <result of folding f and z over [2; 1]> <handler to continue folding leftwards> <handler to abort the traversal>
 
-       f's job would be to check whether 3 matches the element we're searching for
-       (here also 3), and if it does, just evaluate to the result of passing `true` to
+       `f`'s job would be to check whether `3` matches the element we're searching for
+       (here also `3`), and if it does, just evaluate to the result of passing `true` to
        the abort handler. If it doesn't, then evaluate to the result of passing
        `false` to the continue-leftwards handler.
 
        the abort handler. If it doesn't, then evaluate to the result of passing
        `false` to the continue-leftwards handler.
 
-       In this case, f wouldn't need to consult the result of folding f and z over [2;
-       1], since if we had found the element 3 in more rightward positions of the
-       list, we'd have called the abort handler and this application of f to 3 etc
-       would never be needed. However, in other applications the result of folding f
-       and z over the more rightward parts of the list would be needed. Consider if
+       In this case, `f` wouldn't need to consult the result of folding `f` and `z` over `[2;
+       1]`, since if we had found the element `3` in more rightward positions of the
+       list, we'd have called the abort handler and this application of `f` to `3` etc
+       would never be needed. However, in other applications the result of folding `f`
+       and `z` over the more rightward parts of the list would be needed. Consider if
        you were trying to multiply all the elements of the list, and were going to
        you were trying to multiply all the elements of the list, and were going to
-       abort (with the result 0) if you came across any element in the list that was
+       abort (with the result `0`) if you came across any element in the list that was
        zero. If you didn't abort, you'd need to know what the more rightward elements
        of the list multiplied to, because that would affect the answer you passed
        along to the continue-leftwards handler.
 
        zero. If you didn't abort, you'd need to know what the more rightward elements
        of the list multiplied to, because that would affect the answer you passed
        along to the continue-leftwards handler.
 
-       A **version 5** list would encode this kind of fold operation over the list, in
+       A **version 5** list encodes the kind of fold operation we're envisaging here, in
        the same way that v3 (and v4) lists encoded the simpler fold operation.
        the same way that v3 (and v4) lists encoded the simpler fold operation.
-       Roughly, the list [5; 4; 3; 2; 1] would look like this:
+       Roughly, the list `[5;4;3;2;1]` would look like this:
 
 
                \f z continue_leftwards_handler abort_handler.
 
 
                \f z continue_leftwards_handler abort_handler.
-                       <fold f and z over [4; 3; 2; 1]>
+                       <fold f and z over [4;3;2;1]>
                        (\result_of_fold_over_4321. f 5 result_of_fold_over_4321  continue_leftwards_handler abort_handler)
                        abort_handler
 
                        (\result_of_fold_over_4321. f 5 result_of_fold_over_4321  continue_leftwards_handler abort_handler)
                        abort_handler
 
+               ; or, expanding the fold over [4;3;2;1]:
 
                \f z continue_leftwards_handler abort_handler.
                        (\continue_leftwards_handler abort_handler.
 
                \f z continue_leftwards_handler abort_handler.
                        (\continue_leftwards_handler abort_handler.
-                               <fold f and z over [3; 2; 1]>
+                               <fold f and z over [3;2;1]>
                                (\result_of_fold_over_321. f 4 result_of_fold_over_321 continue_leftwards_handler abort_handler)
                                abort_handler
                        )
                        (\result_of_fold_over_4321. f 5 result_of_fold_over_4321  continue_leftwards_handler abort_handler)
                        abort_handler
 
                                (\result_of_fold_over_321. f 4 result_of_fold_over_321 continue_leftwards_handler abort_handler)
                                abort_handler
                        )
                        (\result_of_fold_over_4321. f 5 result_of_fold_over_4321  continue_leftwards_handler abort_handler)
                        abort_handler
 
-               and so on               
+               ; and so on             
                
                
-       Remarks: the `larger_computation_handler` should be supplied as both the
+       Remarks: the `larger_computation` handler should be supplied as both the
        `continue_leftwards_handler` and the `abort_handler` for the leftmost
        `continue_leftwards_handler` and the `abort_handler` for the leftmost
-       application, where the head 5 is supplied to f. Because the result of this
+       application, where the head `5` is supplied to `f`; because the result of this
        application should be passed to the larger computation, whether it's a "fall
        off the left end of the list" result or it's a "I'm finished, possibly early"
        application should be passed to the larger computation, whether it's a "fall
        off the left end of the list" result or it's a "I'm finished, possibly early"
-       result. The `larger_computation_handler` also then gets passed to the next
-       rightmost stage, where the head 4 is supplied to f, as the `abort_handler` to
+       result. The `larger_computation` handler also then gets passed to the next
+       rightmost stage, where the head `4` is supplied to `f`, as the `abort_handler` to
        use if that stage decides it has an early answer.
 
        use if that stage decides it has an early answer.
 
-       Finally, notice that we don't have the result of applying f to 4 etc given as
-       an argument to the application of f to 5 etc. Instead, we pass
+       Finally, notice that we don't have the result of applying `f` to `4` etc given as
+       an argument to the application of `f` to `5` etc. Instead, we pass
 
 
-               (\result_of_fold_over_4321. f 5 result_of_fold_over_4321 one_handler another_handler)
+               (\result_of_fold_over_4321. f 5 result_of_fold_over_4321 <one_handler> <another_handler>)
 
 
-       *to* the application of f to 4 as its "continue" handler. The application of f
-       to 4 can decide whether this handler, or the other, "abort" handler, should be
+       *to* the application of `f` to `4` as its "continue" handler. The application of `f`
+       to `4` can decide whether this handler, or the other, "abort" handler, should be
        given an argument and constitute its result.
 
 
        given an argument and constitute its result.
 
 
@@ -393,8 +402,8 @@ can use.
        of the complex expression semantically depending only on this, not on that. A
        demon evaluator who custom-picked the evaluation order to make things maximally
        bad for you could ensure that all the semantically unnecessary computations got
        of the complex expression semantically depending only on this, not on that. A
        demon evaluator who custom-picked the evaluation order to make things maximally
        bad for you could ensure that all the semantically unnecessary computations got
-       evaluated anyway. At this stage, we don't have any way to prevent that. Later,
-       we'll see ways to semantically guarantee one evaluation order rather than
+       evaluated anyway. We don't have any way to prevent that. Later,
+       we'll see ways to *semantically guarantee* one evaluation order rather than
        another. Though even then the demonic evaluation-order-chooser could make it
        take unnecessarily long to compute the semantically guaranteed result. Of
        course, in any real computing environment you'll know you're dealing with a
        another. Though even then the demonic evaluation-order-chooser could make it
        take unnecessarily long to compute the semantically guaranteed result. Of
        course, in any real computing environment you'll know you're dealing with a
@@ -424,9 +433,6 @@ can use.
                                ; here's the abort_handler
                                larger_computation  in
                let extract_tail = ; left as exercise
                                ; here's the abort_handler
                                larger_computation  in
                let extract_tail = ; left as exercise
-                       ;; for real efficiency, it'd be nice to fuse the apparatus developed
-                       ;; in these v5 lists with the ideas from the v4 lists, above
-                       ;; but that also is left as an exercise
 
        These functions are used like this:
 
 
        These functions are used like this:
 
@@ -440,12 +446,47 @@ can use.
        your reach. And once you have followed it, you'll be well on your way to
        appreciating the full terrible power of continuations.
 
        your reach. And once you have followed it, you'll be well on your way to
        appreciating the full terrible power of continuations.
 
-<!-- (Silly [cultural reference](http://www.newgrounds.com/portal/view/33440).) -->
+       <!-- (Silly [cultural reference](http://www.newgrounds.com/portal/view/33440).) -->
 
        Of course, like everything elegant and exciting in this seminar, [Oleg
        discusses it in much more
        detail](http://okmij.org/ftp/Streams.html#enumerator-stream).
 
 
        Of course, like everything elegant and exciting in this seminar, [Oleg
        discusses it in much more
        detail](http://okmij.org/ftp/Streams.html#enumerator-stream).
 
+       *Comments*:
+
+       1.      The technique deployed here, and in the v2 lists, and in our implementations
+               of pairs and booleans, is known as **continuation-passing style** programming.
+
+       2.      We're still building the list as a right fold, so in a sense the
+               application of `f` to the leftmost element `5` is "outermost". However,
+               this "outermost" application is getting lifted, and passed as a *handler*
+               to the next right application. Which is in turn getting lifted, and
+               passed to its next right application, and so on. So if you
+               trace the evaluation of the `extract_head` function to the list `[5;4;3;2;1]`,
+               you'll see `1` gets passed as a "this is the head sofar" answer to its
+               `continue_handler`; then that answer is discarded and `2` is
+               passed as a "this is the head sofar" answer to *its* `continue_handler`,
+               and so on. All those steps have to be evaluated to finally get the result
+               that `5` is the outer/leftmost head of the list. That's not an efficient way
+               to get the leftmost head.
+
+               We could improve this by building lists as left folds when implementing them
+               as continuation-passing style folds. We'd just replace above:
+       
+                       let make_list = \h t. \f z continue_handler abort_handler.
+                               f h z (\z. t f z continue_handler abort_handler) abort_handler
+
+               now `extract_head` should return the leftmost head directly, using its `abort_handler`:
+
+                       let extract_head = \lst larger_computation. lst
+                                       (\hd sofar continue_handler abort_handler. abort_handler hd)
+                                       junk
+                                       larger_computation
+                                       larger_computation
+
+       3.      To extract tails efficiently, too, it'd be nice to fuse the apparatus developed
+               in these v5 lists with the ideas from the v4 lists, above.
+               But that also is left as an exercise.
 
 
 5.     Implementing (self-balancing) trees
 
 
 5.     Implementing (self-balancing) trees