update week1 notes
[lambda.git] / week1.mdwn
index 1f50036..734bd65 100644 (file)
@@ -430,7 +430,7 @@ Here φ should evaluate to a boolean, and ψ and χ should evaluate to
 We said that that could be taken as shorthand for the following `case`-expression:
 
 `case` φ `of`  
-  `'true then` ψ  
+  `'true then` ψ`;`  
   `'false then` χ  
 `end`
 
@@ -452,7 +452,7 @@ If a `case`-expression gets to the end of its list of patterns, and *none* of th
 
 will both evaluate to `'false`, without any pattern-matching failure.
 
-There's a superficial similarity between the `let`-constructions and the `case`-constructions. Each has a list whose left-hand sides are patterns and right-hand sides are expressions. Each also has an additional expression that stands out in a special position: in `let`-expressions at the end, in `case`-expressions at the beginning. But the relations of these different elements to each other is different. In `let`-expressions, the right-hand sides of the list supply the values that get bound to the variables in the patterns on the left-hand sides. Also, each pattern in the list will get matched, unless there's a pattern-match failure before we get to it. In `case`-expressions, on the other hand, it's the initial expression that supplies the value (or multivalues) that we attempt to match against the pattern, and we stop as soon as we reach a pattern that we can successfully match against. Then the variables in that pattern are bound when evaluating the right-hand side expressions.
+There's a superficial similarity between the `let`-constructions and the `case`-constructions. Each has a list whose left-hand sides are patterns and right-hand sides are expressions. Each also has an additional expression that stands out in a special position: in `let`-expressions at the end, in `case`-expressions at the beginning. But the relations of these different elements to each other is different. In `let`-expressions, the right-hand sides of the list supply the values that get bound to the variables in the patterns on the left-hand sides. Also, each pattern in the list will get matched, unless there's a pattern-match failure before we get to it. In `case`-expressions, on the other hand, it's the initial expression that supplies the value (or multivalues) that we attempt to match against the pattern, and we stop as soon as we reach a pattern that we can successfully match against. Then the variables in that pattern are bound when evaluating the corresponding right-hand side expression.
 
 
 ### Recursive let ###
@@ -516,7 +516,7 @@ For the time being, we will fix this solution by just introducing a special new
   `factorial match` λ `n. case n of 0 then 1; _ then n * factorial (n - 1) end`  
 `in factorial 4`
 
-the initial binding of `factorial` gets ignored, and the `factorial` in the right-hand side of our definition is interpreted to mean the very same function that we are hereby binding to `factorial`. Exactly how this works is a deep and exciting topic, that we will be looking at very closely in a few weeks. For the time being, let's just accept that `letrec` does what we intuitively want when defining functions recursively.
+the initial binding of `factorial` to the identity function gets ignored, and the `factorial` in the right-hand side of our definition is interpreted to mean the very same function that we are hereby binding to `factorial`. Exactly how this works is a deep and exciting topic, that we will be looking at very closely in a few weeks. For the time being, let's just accept that `letrec` does what we intuitively want when defining functions recursively.
 
 **It's important to make sure you say letrec when that's what you want.** You may not *always* want `letrec`, though, if you're ever re-using variables (or doing other things) that rely on the bindings occurring in a specified order. With `letrec`, all the bindings in the construction happen simultaneously. This is why you can say, as Jim did in class:
 
@@ -554,4 +554,43 @@ So for example, if we evaluated `length [10, 20, 30]`, that would give the same
 
 Programmers will sometimes define functions in the second style because it can be evaluated more efficiently than the first style. You don't need to worry about things like efficiency in this seminar. But you should become acquainted with, and comfortable with, both styles of recursive definition.
 
+It may be helpful to contrast these recursive-style definitons to the way one would more naturally define the `length` function in an imperatival language. This uses some constructs we haven't explained yet, but I trust their meaning will be intuitively clear enough.
+
+`let`  
+  `empty? match` λ `xs.` *this definition left as an exercise*;  
+  `tail match` λ `xs.` *this definition left as an exercise*;  
+  `length match` λ `xs. let`  
+                                         `n := 0;`  
+                                         `while not (empty? xs) do`  
+                                           `n := n + 1;`  
+                                           `xs := tail xs`  
+                                         `end`  
+                                      `in n`  
+`in length`
+
+Here there is no recursion. Rather what happens is that we *initialize* the variable `n` with the value `0`, and then so long as our sequence variable `xs` is non-empty, we *increment* that variable `n`, and *overwrite* the variable `xs` with the tail of the sequence that it is then bound to, and repeat in a loop (the `while ... do ... end` construction). This is similar to what happens in our second definition of `length`, using `aux`, but here it happens using *mutation* or *overwriting* the values of variables, and a special looping construction, whereas in the preceding definitions we achieved the same effect instead with recursion.
+
+We will be looking closely at mutation later in the term. For the time being, our focus will instead be on the recursive and *immutable* style of doing things. Meaning no variables get overwritten.
+
+It's helpful to observe that in expressions like:
+
+    let
+      x match 0;
+      y match x + 1;
+      x match x + 1;
+      z match 2 * x
+    in (y, z)
+
+the variable `x` has not been *overwritten* (mutated). Rather, we have *two* variables `x` and its just that the second one is *hiding* the first so long as its scope is in effect. Once its scope expires, the original variable `x` is still in place, with its orginal binding. A different example should help clarify this. What do you think this:
+
+    let
+      x match 0;
+      (y, z) match let
+                     x match x + 1
+                   in (x, 2*x)
+    in ([y, z], x)
+
+evaluates to? Well, the right-hand side of the second binding will evaluate to `(1, 2)`, because it uses the outer binding of `x` to `0` for the right-hand side of its own binding `x match x + 1`. That gives us a new binding of `x` to `1`, which is in place when we evaluate `(x, 2*x)`. That's why the whole right-hand side of the second binding evaluates to `(1, 2)`. So `y` gets bound to `1` and `z` to `2`. But now what is `x` bound to in the final line? The binding of `x` to `1` was in place only until we got to `(x, 2*x)`. After that its scope expired, and the original binding of `x` to `0` reappears. So the final line evaluates to `([1, 2], 0)`.
+
+This is very like what happens in ordinary predicate logic if you say: &exists; `x. F x and (` ∀ `x. G x ) and H x`. The `x` in `F x` and in `H x` are governed by the outermost quantifier, and only the `x` in `G x` is governed by the inner quantifier.