Lecture #10, Tuesday, February 7th
==================================
 Fixing an Overlooked Bug
 Lexical Scope using Racket Closures

# Fixing an Overlooked Bug
Incidentally, this version fixes a bug we had previously in the
substitution version of FLANG:
(run "{with {f {fun {y} {+ x y}}}
{with {x 7}
{call f 1}}}")
This bug was due to our naive `subst`, which doesn't avoid capturing
renames. But note that since that version of the evaluator makes its way
from the outside in, there is no difference in semantics for *valid*
programs  ones that don't have free identifiers.
(Reminder: This was *not* a dynamically scoped language, just a bug that
happened when `x` wasn't substituted away before `f` was replaced with
something that refers to `x`.)

# Lexical Scope using Racket Closures
> [PLAI ยง11] (without the last part about recursion)
An alternative representation for an environment.
We've already seen how firstclass functions can be used to implement
"objects" that contain some information. We can use the same idea to
represent an environment. The basic intuition is  an environment is a
*mapping* (a function) between an identifier and some value. For
example, we can represent the environment that maps `'a` to `1` and `'b`
to `2` (using just numbers for simplicity) using this function:
(: mymap : Symbol > Number)
(define (mymap id)
(cond [(eq? 'a id) 1]
[(eq? 'b id) 2]
[else (error ...)]))
An empty mapping that is implemented in this way has the same type:
(: emptymapping : Symbol > Number)
(define (emptymapping id)
(error ...))
We can use this idea to implement our environments: we only need to
define three things  `EmptyEnv`, `Extend`, and `lookup`. If we manage
to keep the contract to these functions intact, we will be able to
simply plug it into the same evaluator code with no other changes. It
will also be more convenient to define `ENV` as the appropriate function
type for use in the `VAL` type definition instead of using the actual
type:
;; Define a type for functional environments
(definetype ENV = Symbol > VAL)
Now we get to `EmptyEnv`  this is expected to be a function that
expects no arguments and creates an empty environment, one that behaves
like the `emptymapping` function defined above. We could define it like
this (changing the `emptymapping` type to return a `VAL`):
(define (EmptyEnv) emptymapping)
but we can skip the need for an extra definition and simply return an
empty mapping function:
(: EmptyEnv : > ENV)
(define (EmptyEnv)
(lambda (id) (error ...)))
(The unRackety name is to avoid replacing previous code that used the
`EmptyEnv` name for the constructor that was created by the type
definition.)
The next thing we tackle is `lookup`. The previous definition that was
used is:
(: lookup : Symbol ENV > VAL)
(define (lookup name env)
(cases env
[(EmptyEnv) (error 'lookup "no binding for ~s" name)]
[(Extend id val restenv)
(if (eq? id name) val (lookup name restenv))]))
How should it be modified now? Easy  an environment is a mapping: a
Racket function that will do the searching job itself. We don't need to
modify the contract since we're still using `ENV`, except a different
implementation for it. The new definition is:
(: lookup : Symbol ENV > VAL)
(define (lookup name env)
(env name))
Note that `lookup` does almost nothing  it simply delegates the real
work to the `env` argument. This is a good hint for the error message
that empty mappings should throw 
(: EmptyEnv : > ENV)
(define (EmptyEnv)
(lambda (id) (error 'lookup "no binding for ~s" id)))
Finally, `Extend`  this was previously created by the variant case of
the ENV type definition:
[Extend Symbol VAL ENV]
keeping the same type that is implied by this variant means that the new
`Extend` should look like this:
(: Extend : Symbol VAL ENV > ENV)
(define (Extend id val restenv)
...)
The question is  how do we extend a given environment? Well, first,
we know that the result should be mapping  a `symbol > VAL` function
that expects an identifier to look for:
(: Extend : Symbol VAL ENV > ENV)
(define (Extend id val restenv)
(lambda (name)
...))
Next, we know that in the generated mapping, if we look for `id` then
the result should be `val`:
(: Extend : Symbol VAL ENV > ENV)
(define (Extend id val restenv)
(lambda (name)
(if (eq? name id)
val
...)))
If the `name` that we're looking for is not the same as `id`, then we
need to search through the previous environment:
(: Extend : Symbol VAL ENV > ENV)
(define (Extend id val restenv)
(lambda (name)
(if (eq? name id)
val
(lookup name restenv))))
But we know what `lookup` does  it simply delegates back to the
mapping function (which is our `rest` argument), so we can take a direct
route instead:
(: Extend : Symbol VAL ENV > ENV)
(define (Extend id val restenv)
(lambda (name)
(if (eq? name id)
val
(restenv name)))) ; same as (lookup name restenv)
To see how all this works, try out extending an empty environment a few
times and examine the result. For example, the environment that we began
with:
(define (mymap id)
(cond [(eq? 'a id) 1]
[(eq? 'b id) 2]
[else (error ...)]))
behaves in the same way (if the type of values is numbers) as
(Extend 'a 1 (Extend 'b 2 (EmptyEnv)))
The new code is now the same, except for the environment code:
#lang pl
#
The grammar:
::=
 { + }
 {  }
 { * }
 { / }
 { with { } }

 { fun { } }
 { call }
Evaluation rules:
eval(N,env) = N
eval({+ E1 E2},env) = eval(E1,env) + eval(E2,env)
eval({ E1 E2},env) = eval(E1,env)  eval(E2,env)
eval({* E1 E2},env) = eval(E1,env) * eval(E2,env)
eval({/ E1 E2},env) = eval(E1,env) / eval(E2,env)
eval(x,env) = lookup(x,env)
eval({with {x E1} E2},env) = eval(E2,extend(x,eval(E1,env),env))
eval({fun {x} E},env) = <{fun {x} E}, env>
eval({call E1 E2},env1)
= eval(Ef,extend(x,eval(E2,env1),env2))
if eval(E1,env1) = <{fun {x} Ef}, env2>
= error! otherwise
#
(definetype FLANG
[Num Number]
[Add FLANG FLANG]
[Sub FLANG FLANG]
[Mul FLANG FLANG]
[Div FLANG FLANG]
[Id Symbol]
[With Symbol FLANG FLANG]
[Fun Symbol FLANG]
[Call FLANG FLANG])
(: parsesexpr : Sexpr > FLANG)
;; parses sexpressions into FLANGs
(define (parsesexpr sexpr)
(match sexpr
[(number: n) (Num n)]
[(symbol: name) (Id name)]
[(cons 'with more)
(match sexpr
[(list 'with (list (symbol: name) named) body)
(With name (parsesexpr named) (parsesexpr body))]
[else (error 'parsesexpr "bad `with' syntax in ~s" sexpr)])]
[(cons 'fun more)
(match sexpr
[(list 'fun (list (symbol: name)) body)
(Fun name (parsesexpr body))]
[else (error 'parsesexpr "bad `fun' syntax in ~s" sexpr)])]
[(list '+ lhs rhs) (Add (parsesexpr lhs) (parsesexpr rhs))]
[(list ' lhs rhs) (Sub (parsesexpr lhs) (parsesexpr rhs))]
[(list '* lhs rhs) (Mul (parsesexpr lhs) (parsesexpr rhs))]
[(list '/ lhs rhs) (Div (parsesexpr lhs) (parsesexpr rhs))]
[(list 'call fun arg)
(Call (parsesexpr fun) (parsesexpr arg))]
[else (error 'parsesexpr "bad syntax in ~s" sexpr)]))
(: parse : String > FLANG)
;; parses a string containing a FLANG expression to a FLANG AST
(define (parse str)
(parsesexpr (string>sexpr str)))
;; Types for environments, values, and a lookup function
(definetype VAL
[NumV Number]
[FunV Symbol FLANG ENV])
;; Define a type for functional environments
(definetype ENV = Symbol > VAL)
(: EmptyEnv : > ENV)
(define (EmptyEnv)
(lambda (id) (error 'lookup "no binding for ~s" id)))
(: Extend : Symbol VAL ENV > ENV)
;; extend a given environment cache with a new binding
(define (Extend id val restenv)
(lambda (name)
(if (eq? name id)
val
(restenv name))))
(: lookup : Symbol ENV > VAL)
;; lookup a symbol in an environment, return its value or throw an
;; error if it isn't bound
(define (lookup name env)
(env name))
(: NumV>number : VAL > Number)
;; convert a FLANG runtime numeric value to a Racket one
(define (NumV>number val)
(cases val
[(NumV n) n]
[else (error 'arithop "expected a number, got: ~s" val)]))
(: arithop : (Number Number > Number) VAL VAL > VAL)
;; gets a Racket numeric binary operator, and uses it within a NumV
;; wrapper
(define (arithop op val1 val2)
(NumV (op (NumV>number val1) (NumV>number val2))))
(: eval : FLANG ENV > VAL)
;; evaluates FLANG expressions by reducing them to values
(define (eval expr env)
(cases expr
[(Num n) (NumV n)]
[(Add l r) (arithop + (eval l env) (eval r env))]
[(Sub l r) (arithop  (eval l env) (eval r env))]
[(Mul l r) (arithop * (eval l env) (eval r env))]
[(Div l r) (arithop / (eval l env) (eval r env))]
[(With boundid namedexpr boundbody)
(eval boundbody
(Extend boundid (eval namedexpr env) env))]
[(Id name) (lookup name env)]
[(Fun boundid boundbody)
(FunV boundid boundbody env)]
[(Call funexpr argexpr)
(let ([fval (eval funexpr env)])
(cases fval
[(FunV boundid boundbody fenv)
(eval boundbody
(Extend boundid (eval argexpr env) fenv))]
[else (error 'eval "`call' expects a function, got: ~s"
fval)]))]))
(: run : String > Number)
;; evaluate a FLANG program contained in a string
(define (run str)
(let ([result (eval (parse str) (EmptyEnv))])
(cases result
[(NumV n) n]
[else (error 'run "evaluation returned a nonnumber: ~s"
result)])))
;; tests
(test (run "{call {fun {x} {+ x 1}} 4}")
=> 5)
(test (run "{with {add3 {fun {x} {+ x 3}}}
{call add3 1}}")
=> 4)
(test (run "{with {add3 {fun {x} {+ x 3}}}
{with {add1 {fun {x} {+ x 1}}}
{with {x 3}
{call add1 {call add3 x}}}}}")
=> 7)
(test (run "{with {identity {fun {x} x}}
{with {foo {fun {x} {+ x 1}}}
{call {call identity foo} 123}}}")
=> 124)
(test (run "{with {x 3}
{with {f {fun {y} {+ x y}}}
{with {x 5}
{call f 4}}}}")
=> 7)
(test (run "{call {with {x 3}
{fun {y} {+ x y}}}
4}")
=> 7)
(test (run "{with {f {with {x 3} {fun {y} {+ x y}}}}
{with {x 100}
{call f 4}}}")
=> 7)
(test (run "{call {call {fun {x} {call x 1}}
{fun {x} {fun {y} {+ x y}}}}
123}")
=> 124)