week4 debugging more
[lambda.git] / advanced_lambda.mdwn
1 [[!toc]]
2
3
4 #Version 4 lists: Efficiently extracting tails#
5
6 An advantage of the v3 lists and v3 (aka "Church") numerals is that they
7 have a recursive capacity built into their skeleton. So for many natural
8 operations on them, you won't need to use a fixed point combinator. Why is
9 that an advantage? Well, if you use a fixed point combinator, then the terms
10 you get
11 won't be strongly normalizing: whether their reduction stops at a normal form
12 will depend on what evaluation order you use. Our online [[lambda evaluator]]
13 uses normal-order reduction, so it finds a normal form if there's one to be
14 had. But if you want to build lambda terms in, say, Scheme, and you wanted to
15 roll your own recursion as we've been doing, rather than relying on Scheme's
16 native `let rec` or `define`, then you can't use the fixed-point combinators
17 `Y` or <code>&Theta;</code>. Expressions using them will have non-terminating
18 reductions, with Scheme's eager/call-by-value strategy. There are other
19 fixed-point combinators you can use with Scheme (in the [week 3 notes](/week3/#index7h2) they
20 were <code>Y&prime;</code> and <code>&Theta;&prime;</code>. But even with
21 them, evaluation order still matters: for some (admittedly unusual)
22 evaluation strategies, expressions using them will also be non-terminating.
23
24 The fixed-point combinators may be the conceptual stars. They are cool and
25 mathematically elegant. But for efficiency and implementation elegance, it's
26 best to know how to do as much as you can without them. (Also, that knowledge
27 could carry over to settings where the fixed point combinators are in
28 principle unavailable.)
29
30 This is why the v3 lists and numbers are so lovely. However, one disadvantage
31 to them is that it's relatively inefficient to extract a list's tail, or get a
32 number's predecessor. To get the tail of the list `[a;b;c;d;e]`, one will
33 basically be performing some operation that builds up the tail afresh: at
34 different stages, one will have built up `[e]`, then `[d;e]`, then `[c;d;e]`, and
35 finally `[b;c;d;e]`. With short lists, this is no problem, but with longer lists
36 it takes longer and longer. And it may waste more of your computer's memory
37 than you'd like. Similarly for obtaining a number's predecessor.
38
39 The v1 lists and numbers on the other hand, had the tail and the predecessor
40 right there as an element, easy for the taking. The problem was just that the
41 v1 lists and numbers didn't have recursive capacity built into them, in the
42 way the v3 implementations do.
43
44 A clever approach would marry these two strategies.
45
46 Version 3 makes the list `[a;b;c;d;e]` look like this:
47
48         \f z. f a (f b (f c (f d (f e z))))
49
50 or in other words:
51
52         \f z. f a <the result of folding f and z over the tail>
53
54 Instead we could make it look like this:
55
56         \f z. f a <the tail itself> <the result of folding f and z over the tail>
57
58 That is, now `f` is a function expecting *three* arguments: the head of the
59 current list, the tail of the current list, and the result of continuing to
60 fold `f` over the tail, with a given base value `z`.
61
62 Call this a **version 4** list. The empty list can be the same as in v3:
63
64 <pre><code>empty &equiv; \f z. z</code></pre>
65
66 The list constructor would be:
67
68 <pre><code>make_list &equiv; \h t. \f z. f h t (t f z)</code></pre>
69
70 It differs from the version 3 `make_list` only in adding the extra argument
71 `t` to the new, outer application of `f`.
72
73 Similarly, `five` as a v3 or Church numeral looks like this:
74
75         \s z. s (s (s (s (s z))))
76
77 or in other words:
78
79         \s z. s <the result of applying s to z (pred 5)-many times>
80
81 Instead we could make it look like this:
82
83         \s z. s <pred 5> <the result of applying s to z (pred 5)-many times>
84
85 That is, now `s` is a function expecting *two* arguments: the predecessor of the
86 current number, and the result of continuing to apply `s` to the base value `z`
87 predecessor-many times.
88
89 Jim had the pleasure of "inventing" these implementations himself. However,
90 unsurprisingly, he wasn't the first to do so. See for example [Oleg's report
91 on P-numerals](http://okmij.org/ftp/Computation/lambda-calc.html#p-numerals).
92
93