tweak calc improvements
[lambda.git] / advanced_topics / calculator_improvements.mdwn
index 4ddff27..68bd39d 100644 (file)
@@ -57,7 +57,7 @@ We'll switch over to using variable `g` for assignment functions, which is a con
        type bound_value = expressed_value;;
        type assignment = (char * bound_value) list;;
 
-Here's where we should be now. We expand some of the clauses in the `eval` function for clarity:
+Here's where we should be now. We expand some of the clauses in the `eval` function for clarity, and we rename a few variables:
 
        type term = Constant of int
                | Multiplication of (term * term)
@@ -79,14 +79,14 @@ Here's where we should be now. We expand some of the clauses in the `eval` funct
                let Int value1 = eval t1 g
                in let Int value2 = eval t2 g
                in Int (value1 + value2)
-       | Variable c ->
-               (* we don't handle cases where g doesn't bind c to any value *)
-               List.assoc c g
-       | Let (c, t1, t2) ->
-               (* evaluate t2 under a new assignment where c has been bound to
+       | Variable var ->
+               (* we don't handle cases where g doesn't bind var to any value *)
+               List.assoc var g
+       | Let (var_to_bind, t1, t2) ->
+               (* evaluate t2 under a new assignment where var_to_bind has been bound to
                   the result of evaluating t1 under the current assignment *)
                let value1 = eval t1 g
-               in let g' = (c, value1) :: g
+               in let g' = (var_to_bind, value1) :: g
                in eval t2 g'
        | Iszero t1 ->
                (* we don't handle cases where t1 doesn't evaluate to an Int *)
@@ -120,7 +120,7 @@ We can begin with our language:
                | Lambda of (char * term)
                | Apply of (term * term);;
 
-Next, we need to expand our stock of `expressed_value`s to include function values as well. How should we think of these? We've several times mentioned the issue of how to handle free variables in a function's body, like the `x` in `lambda y -> y + x`. We'll follow the usual functional programming standard for these (known as "lexical scoping"), which keeps track of what value `x` has in the function expression's lexical environment. That shouldn't get shadowed by any different value `x` may have when the function value is later applied. So:
+Next, we need to expand our stock of `expressed_value`s to include function values as well. How should we think of these? We've several times mentioned the issue of how to handle free variables in a function's body, like the `x` in `lambda y -> y + x`. We'll follow the usual functional programming standard for these (known as "lexical scoping"), which keeps track of what value `x` has in the function declaration's lexical environment. That shouldn't get shadowed by any different value `x` may have when the function value is later applied. So:
 
        let x = 1 in let f = lambda y -> y + x in let x = 2 in apply f 2
 
@@ -143,11 +143,11 @@ Now our evaluation function needs two further clauses to interpret the two new e
 
        let rec eval (t : term) (g: assignment) = match t with
        ...
-       | Lambda(c, t1) -> Closure (c, t1, g)
+       | Lambda(arg_var, t1) -> Closure (arg_var, t1, g)
        | Apply(t1, t2) ->
-               let value2 = eval t2 g
                (* we don't handle cases where t1 doesn't evaluate to a function value *)
-               in let Closure (arg_var, body, savedg) = eval t1 g
+               let Closure (arg_var, body, savedg) = eval t1 g
+               in let value2 = eval t2 g
                (* evaluate body under savedg, except with arg_var bound to value2 *)
                in let savedg' = (arg_var, value2) :: savedg
                in eval body savedg';;
@@ -157,7 +157,7 @@ Now our evaluation function needs two further clauses to interpret the two new e
 
 There are different ways to include recursion in our calculator. First, let's imagine our language expanded like this:
 
-       let x = 1 in letrec f = lambda y -> if iszero y then x else y * f (y - 1) in f 3
+       let x = 1 in letrec f = lambda y -> if iszero y then x else y * apply f (y - 1) in apply f 3
 
 where the AST would be:
 
@@ -184,7 +184,7 @@ Here is the expanded definition for our language type:
                | Apply of (term * term)
                | Letrec of (char * term * term);;
 
-Now consider what we'll need to do when evaluating a term like `Letrec ('f', Lambda (...), t2)`. The subterm `Lambda (...)` will evaluate to something of the form `Closure ('y', body, savedg)`, where `f` may occur free in `body`. What we'll want to do is to ensure that when `body` is applied, it's applied using not the assignment `savedg` but a modified assignment `savedg'` which binds `f` to this very function value. That is, we want to bind `f` not to:
+Now consider what we'll need to do when evaluating a term like `Letrec ('f', Lambda (...), t2)`. The subterm `Lambda (...)` will evaluate to something of the form `Closure ('y', body, savedg)`, where `Variable 'f'` may occur free in `body`. What we'll want to do is to ensure that when `body` is applied, it's applied using not the assignment `savedg` but a modified assignment `savedg'` which binds `'f'` to this very function value. That is, we want to bind `'f'` not to:
 
        Closure ('y', body, savedg)
 
@@ -197,9 +197,9 @@ but instead to:
 
 Except, this isn't quite right. It's almost what we want, but not exactly. Can you see the flaw?
 
-The flaw is this: inside `new_closure`, what is `f` bound to? It's bound by `savedg'` to `orig_closure`, which in turn leaves `f` free (or bound to whatever existing value it had according to `savedg`). This isn't what we want. It'll break if we need to make recursive calls to `f` which go more than two levels deep.
+The flaw is this: inside `new_closure`, what is `'f'` bound to? It's bound by `savedg'` to `orig_closure`, which in turn leaves `'f'` free (or bound to whatever existing value it had according to `savedg`). This isn't what we want. It'll break if we need to make applications of `Variable 'f'` which recurse more than once.
 
-What we really want is for `f` to be bound to `new_closure`, something like this:
+What we really want is for `'f'` to be bound to `new_closure`, something like this:
 
        let rec new_closure = Closure ('y', body, ('f', new_closure) :: savedg)
        in new_closure
@@ -208,11 +208,11 @@ And as a matter of fact, OCaml *does* permit us to recursively define cyclical l
 
        let rec eval (t : term) (g: assignment) = match t with
        ...
-       | Letrec (c, t1, t2) ->
+       | Letrec (var_to_bind, t1, t2) ->
                (* we don't handle cases where t1 doesn't evaluate to a function value *)
                let Closure (arg_var, body, savedg) = eval t1 g
-        in let rec new_closure = Closure (arg_var, body, (c, new_closure) :: savedg)
-        in let g' = (c, new_closure) :: g
+        in let rec new_closure = Closure (arg_var, body, (var_to_bind, new_closure) :: savedg)
+        in let g' = (var_to_bind, new_closure) :: g
                in eval t2 g';;
         
 However, this is a somewhat exotic ability in a programming language, so it would be good to work out how to interpret `Letrec(...)` forms without relying on it.
@@ -220,15 +220,15 @@ However, this is a somewhat exotic ability in a programming language, so it woul
 If we implemented assignments as functions rather than as lists of pairs, the corresponding move would be less exotic. In that case, our `Let(...)` and `Letrec(...)` clauses would look something like this:
 
 
-       | Let (c, t1, t2) ->
+       | Let (var_to_bind, t1, t2) ->
                let value1 = eval t1 g
-               in let g' = fun var -> if var = c then value1 else g var
+               in let g' = fun var -> if var = var_to_bind then value1 else g var
                in eval t2 g'
        ...
-       | Letrec (c, t1, t2) ->
+       | Letrec (var_to_bind, t1, t2) ->
                let Closure (arg_var, body, savedg) = eval t1 g
-               in let rec savedg' = fun var -> if var = c then Closure (arg_var, body, savedg') else savedg var
-               in let g' = fun var -> if var = c then Closure (arg_var, body, savedg') else g var
+               in let rec savedg' = fun var -> if var = var_to_bind then Closure (arg_var, body, savedg') else savedg var
+               in let g' = fun var -> if var = var_to_bind then Closure (arg_var, body, savedg') else g var
                in eval t2 g';;
 
 and this is just a run-of-the-mill use of recursive functions. However, for this exercise we'll continue using lists of pairs, and work out how to interpret `Letrec(...)` forms using them.
@@ -245,7 +245,7 @@ The way we'll do this is that, when we bind a value to a variable, we'll keep tr
                Letrec ('f', Lambda ('y', Variable 'f')),
                ...)
 
-In the first case, an application of `f` to any argument should evaluate to `Int 1`; in the second case, it should evaluate to the same function closure that `f` evaluates to. We'll keep track of which way a variable was bound by expanding our `bound_value` type:
+In the first case, an application of `Variable 'f'` to any argument should evaluate to `Int 1`; in the second case, it should evaluate to the same function closure that `Variable 'f'` evaluates to. We'll keep track of which way a variable was bound by expanding our `bound_value` type:
 
        type expressed_value = Int of int | Bool of bool | Closure of char * term * assignment
        and bound_value = Nonrecursive of expressed_value |
@@ -253,40 +253,40 @@ In the first case, an application of `f` to any argument should evaluate to `Int
        and assignment = (char * bound_value) list;;
 
 
-Since we're not permitting ourselves OCaml's ability to recursively define cyclical lists, we're not going to be able to update the saved assignment in a closure when that closure is recursively bound to a variable. Instead, we'll just make a note of what variable `f` is supposed to be the recursively bound one---by binding it not to `Nonrecursive (Closure (arg_var, body, savedg))` but rather to `Recursive_Closure ('f', arg_var, body, savedg)`. We'll do the work to make the saved assignment recursive in the right way *later*, when we *evaluate* `f`. The result will look like this:
+Since we're not permitting ourselves OCaml's ability to recursively define cyclical lists, we're not going to be able to update the saved assignment in a closure when that closure is recursively bound to a variable. Instead, we'll just make a note that variable `'f'` is supposed to be the recursively bound one---by binding it not to `Nonrecursive (Closure (arg_var, body, savedg))` but rather to `Recursive_Closure ('f', arg_var, body, savedg)`. We'll do the work to make the saved assignment recursive in the right way *later*, when we *evaluate* `Variable 'f'`. The result will look like this:
 
        let rec eval (t : term) (g: assignment) = match t with
        ...
-       | Variable c -> (
-               (* we don't handle cases where g doesn't bind c to any value *)
-               match List.assoc c g with
+       | Variable var -> (
+               (* we don't handle cases where g doesn't bind var to any value *)
+               match List.assoc var g with
           | Nonrecursive value -> value
           | Recursive_Closure (self_var, arg_var, body, savedg) as rec_closure ->
                          (* we update savedg to bind self_var to rec_closure here *)
               let savedg' = (self_var, rec_closure) :: savedg
               in Closure (arg_var, body, savedg')
         )
-       | Let (c, t1, t2) ->
-               (* evaluate t2 under a new assignment where c has been bound to
+       | Let (var_to_bind, t1, t2) ->
+               (* evaluate t2 under a new assignment where var_to_bind has been bound to
            the result of evaluating t1 under the current assignment *)
                let value1 = eval t1 g
                (* we have to wrap value1 in Nonrecursive *)
-               in let g' = (c, Nonrecursive value1) :: g
+               in let g' = (var_to_bind, Nonrecursive value1) :: g
                in eval t2 g'
        ...
-       | Lambda(c, t1) -> Closure (c, t1, g)
+       | Lambda(arg_var, t1) -> Closure (arg_var, t1, g)
        | Apply(t1, t2) ->
-               let value2 = eval t2 g
                (* we don't handle cases where t1 doesn't evaluate to a function value *)
-               in let Closure (arg_var, body, savedg) = eval t1 g
+               let Closure (arg_var, body, savedg) = eval t1 g
+               in let value2 = eval t2 g
                (* evaluate body under savedg, except with arg_var bound to Nonrecursive value2 *)
                in let savedg' = (arg_var, Nonrecursive value2) :: savedg
                in eval body savedg'
-       | Letrec (c, t1, t2) ->
+       | Letrec (var_to_bind, t1, t2) ->
                (* we don't handle cases where t1 doesn't evaluate to a function value *)
                let Closure (arg_var, body, savedg) = eval t1 g
-        (* evaluate t2 under a new assignment where c has been recursively bound to that function value *) 
-               in let g' = (c, Recursive_Closure(c, arg_var, body, savedg)) :: g
+        (* evaluate t2 under a new assignment where var_to_bind has been recursively bound to that function value *) 
+               in let g' = (var_to_bind, Recursive_Closure(var_to_bind, arg_var, body, savedg)) :: g
                in eval t2 g';;