tweaked week3
[lambda.git] / week3.mdwn
index 07a14f5..3d336f6 100644 (file)
@@ -2,8 +2,9 @@
 
 How could we compute the length of a list? Without worrying yet about what lambda-calculus implementation we're using for the list, the basic idea would be to define this recursively:
 
-       the empty list has length 0
-       any non-empty list has length 1 + (the length of its tail)
+>      the empty list has length 0
+
+>      any non-empty list has length 1 + (the length of its tail)
 
 In OCaml, you'd define that like this:
 
@@ -13,16 +14,20 @@ In OCaml, you'd define that like this:
 
 In Scheme you'd define it like this:
 
-       (letrec [(get_length (lambda (lst) (if (null? lst) 0 (+ 1 (get_length (cdr lst))))))]
+       (letrec [(get_length
+                               (lambda (lst) (if (null? lst) 0 [+ 1 (get_length (cdr lst))] )) )]
                ... ; here you go on to use the function "get_length"
        )
 
 Some comments on this:
 
-       1. `null?` is Scheme's way of saying `isempty`. That is, `(null? lst)` returns true (which Scheme writes as `#t`) iff `lst` is the empty list (which Scheme writes as `'()` or `(list)`).
-       2. `cdr` is function that gets the tail of a Scheme list. (By definition, it's the function for getting the second member of an ordered pair. It just turns out to return the tail of a list because of the particular way Scheme implements lists.)
+1. `null?` is Scheme's way of saying `isempty`. That is, `(null? lst)` returns true (which Scheme writes as `#t`) iff `lst` is the empty list (which Scheme writes as `'()` or `(list)`).
+
+2. `cdr` is function that gets the tail of a Scheme list. (By definition, it's the function for getting the second member of an ordered pair. It just turns out to return the tail of a list because of the particular way Scheme implements lists.)
 
-What is the `let rec` in the OCaml code and the `letrec` in the Scheme code? These work like the `let` expressions we've already seen, except that they let you use the variable `get_length` *inside* the body of the function being bound to it---with the understanding that it will there refer to the same function that you're then in the process of binding to `get_length`. In OCaml:
+3.     I alternate between `[ ]`s and `( )`s in the Scheme code just to make it more readable. These have no syntactic difference.
+
+What is the `let rec` in the OCaml code and the `letrec` in the Scheme code? These work like the `let` expressions we've already seen, except that they let you use the variable `get_length` *inside* the body of the function being bound to it---with the understanding that it will there refer to the same function that you're then in the process of binding to `get_length`. So our recursively-defined function works the way we'd expect it to. In OCaml:
 
        let rec get_length = fun lst ->
                if lst == [] then 0 else 1 + get_length (tail lst)
@@ -31,9 +36,9 @@ What is the `let rec` in the OCaml code and the `letrec` in the Scheme code? The
 
 In Scheme:
 
-       (letrec [(get_length 
-                       (lambda (lst) (if (null? lst) 0 (+ 1 (get_length (cdr lst))) ) )i
-               )] (get_length (list 20 30)))
+       (letrec [(get_length 
+                               (lambda (lst) (if (null? lst) 0 [+ 1 (get_length (cdr lst))] )) )]
+                       (get_length (list 20 30)))
        ; this evaluates to 2
        
 If you instead use an ordinary `let` (or `let*`), here's what would happen, in OCaml:
@@ -45,9 +50,9 @@ If you instead use an ordinary `let` (or `let*`), here's what would happen, in O
 
 Here's Scheme:
 
-       (let* [(get_length 
-                       (lambda (lst) (if (null? lst) 0 (+ 1 (get_length (cdr lst))) ) )
-               )] (get_length (list 20 30)))
+       (let* [(get_length 
+                               (lambda (lst) (if (null? lst) 0 [+ 1 (get_length (cdr lst))] )) )]
+                       (get_length (list 20 30)))
        ; fails with error "reference to undefined identifier: get_length"
 
 Why? Because we said that constructions of this form:
@@ -75,7 +80,7 @@ And indeed, if you tried to define `get_length` in the lambda calculus, how woul
 
        \lst. (isempty lst) zero (add one (get-length (extract-tail lst)))
 
-we've defined all of `isempty`, `zero`, `add`, `one`, and `extract-tail` in earlier discussion. But what about `get-length`? That's not yet defined! In fact, that's the very formula we're trying here to specify.
+We've defined all of `isempty`, `zero`, `add`, `one`, and `extract-tail` in earlier discussion. But what about `get-length`? That's not yet defined! In fact, that's the very formula we're trying here to specify.
 
 What we really want to do is something like this:
 
@@ -95,10 +100,10 @@ So how could we do it? And how do OCaml and Scheme manage to do it, with their `
 
 2.     If you tried this in Scheme:
 
-               (define get_length 
-                               (lambda (lst) (if (null? lst) 0 (+ 1 (get_length (cdr lst))) ) ))
-
-               (get_length (list 20 30))
+               (define get_length 
+                               (lambda (lst) (if (null? lst) 0 [+ 1 (get_length (cdr lst))] )) )
+               
+               (get_length (list 20 30))
 
        You'd find that it works! This is because `define` in Scheme is really shorthand for `letrec`, not for plain `let` or `let*`. So we should regard this as cheating, too.
 
@@ -106,7 +111,7 @@ So how could we do it? And how do OCaml and Scheme manage to do it, with their `
 
                \lst. lst (\x sofar. successor sofar) zero
 
-       What's happening here? We start with the value zero, then we apply the function `\x sofar. successor sofar` to the two arguments <code>x<sub>n</sub></code> and `zero`, where <code>x<sub>n</sub></code> is the last element of the list. This gives us `successor zero`, or `one`. That's the value we've accumuluted "so far." Then we go apply the function `\x sofar. successor sofar` to the two arguments <code>x<sub>n-1</sub></code> and the value `one` that we've accumulates "so far." This gives us `two`. We continue until we get to the start of the list. The value we've then built up "so far" will be the length of the list.
+       What's happening here? We start with the value zero, then we apply the function `\x sofar. successor sofar` to the two arguments <code>x<sub>n</sub></code> and `zero`, where <code>x<sub>n</sub></code> is the last element of the list. This gives us `successor zero`, or `one`. That's the value we've accumuluted "so far." Then we go apply the function `\x sofar. successor sofar` to the two arguments <code>x<sub>n-1</sub></code> and the value `one` that we've accumulated "so far." This gives us `two`. We continue until we get to the start of the list. The value we've then built up "so far" will be the length of the list.
 
 We can use similar techniques to define many recursive operations on lists and numbers. The reason we can do this is that our "version 3," fold-based implementation of lists, and Church's implementations of numbers, have a internal structure that *mirrors* the common recursive operations we'd use lists and numbers for.
 
@@ -148,7 +153,7 @@ In the lambda calculus, we say a fixed point of an expression `f` is any formula
 
 What is a fixed point of the identity combinator I?
 
-It's a theorem of the lambda calculus that every formula has a fixed point. In fact, it will have infinitely many, syntactically distinct fixed points. And we don't just know that they exist: for any given formula, we can name many of them.
+It's a theorem of the lambda calculus that every formula has a fixed point. In fact, it will have infinitely many, non-equivalent fixed points. And we don't just know that they exist: for any given formula, we can name many of them.
 
 Yes, even the formula that you're using the define the successor function will have a fixed point. Isn't that weird? Think about how it might be true.
 
@@ -160,7 +165,7 @@ who knows what we'd get back? Perhaps there's some non-number-representing formu
 
 Yes! That's exactly right. And which formula this is will depend on the particular way you've implemented the successor function.
 
-Moreover, the recipes that enable us to name fixed points for any given formula aren't *guaranteed* to give us *terminating* fixed points. They might give us formulas X such that neither `X` nor `f X` have normal forms. (Indeed, what they give us for the square function isn't any of the Church numbers, but is rather an expression with no normal form.) However, if we take care we can ensure that we *do* get terminating fixed points. And this gives us a principled, fully general strategy for doing recursion. It lets us define even functions like the Ackermann function, which were until now out of our reach. It would let us define arithmetic and list functions on the "version 1" and "version 2" implementations, where it wasn't always clear how to force the computation to "keep going."
+Moreover, the recipes that enable us to name fixed points for any given formula aren't *guaranteed* to give us *terminating* fixed points. They might give us formulas X such that neither `X` nor `f X` have normal forms. (Indeed, what they give us for the square function isn't any of the Church numerals, but is rather an expression with no normal form.) However, if we take care we can ensure that we *do* get terminating fixed points. And this gives us a principled, fully general strategy for doing recursion. It lets us define even functions like the Ackermann function, which were until now out of our reach. It would let us define arithmetic and list functions on the "version 1" and "version 2" implementations, where it wasn't always clear how to force the computation to "keep going."
 
 [Explain in terms of an arbitrary fixed point combinator &Psi;.]