The following is a sample calculation showing how procedures, closures and environments interact. Except for the renamings of environments, it was automatically generated by the code in $PUB/lib/ch3-5-reducer.scm, by using the run procedure on the string that is the expression to be evaluated. You can use $PUB/lib/ch3-5-reducer.scm to get more of a feel for what is going on, by using it on simpler expressions, even interactively (with the read-eval-print procedure). There are several conventions used in the calculation: - Abstract syntax trees are shown in double angle brackets, <>, as on pp. 87-88 of EOPL2e. - Closures are shown as if they were expressions with 3 parts, a list of identifiers, an AST for the body, and an environment. For example, (closure '(x) <> init-env) is a closure with list of formal parameters (x), AST <>, and environment init-env - Environments are named as soon as they are introduced. The initial environment is named init-env, and other environments are named env1, etc., with the names introduced by a reason of the following form. = { by ..., letting env3 = ... } Such names are global to the calculation. - Go-back-to expressions, which are expressions that are being evaluated in an extended environment are shown as if they were expressions with two parts, an expression AST and an environment. When their evaluation is finished, the interpreter continues with the envronment environment in the second part For example, (go-back-to <<+(x, y)>> env2) is a go-back-to expression, representing the computation of <<+(x, y)>>, in the current environment, *not* env2. The computation will go back to using the env2 when +(x,y) has been fully evaluatied. These are necessary for proper scoping. Here we go... (eval-expression <> init-env) = { by proc-exp case of eval-expression } (eval-expression <> init-env) in let x = 3 in let add3 = (cadd x) in let x = 7 cadd = proc() 0 in (add3 x)>> init-env) = { by let-exp case of eval-expression, letting env1 = (extend-env '(cadd) (list (closure '(x) <> init-env)) init-env) } (eval-expression <> env1) = { by let-exp case of eval-expression, letting env2 = (extend-env '(x) (list 3) env1) } (eval-expression <> env2) = { by var-exp case of eval-expression } (eval-expression <> init-env) x) in let x = 7 cadd = proc() 0 in (add3 x)>> env2) = { by var-exp case of eval-expression } (eval-expression <> init-env) 3) in let x = 7 cadd = proc() 0 in (add3 x)>> env2) = { by app-exp case of eval-expression, letting env3 = (extend-env '(x) (list 3) init-env) } (eval-expression <> env2) in let x = 7 cadd = proc() 0 in (add3 x)>> env3) = { by proc-exp case of eval-expression } (eval-expression <> env3)>> env2) in let x = 7 cadd = proc() 0 in (add3 x)>> env3) = { by resuming the computation using the old environment } (eval-expression <> env3) in let x = 7 cadd = proc() 0 in (add3 x)>> env2) = { by let-exp case of eval-expression, letting env4 = (extend-env '(add3) (list (closure '(y) <<+(x, y)>> env3)) env2) } (eval-expression <> env4) = { by proc-exp case of eval-expression } (eval-expression <> env4) in (add3 x)>> env4) = { by let-exp case of eval-expression, letting env5 = (extend-env '(x cadd) (list 7 (closure '() <<0>> env4)) env4) } (eval-expression <<(add3 x)>> env5) = { by var-exp case of eval-expression } (eval-expression <<((closure '(y) <<+(x, y)>> env3) x)>> env5) = { by var-exp case of eval-expression } (eval-expression <<((closure '(y) <<+(x, y)>> env3) 7)>> env5) = { by app-exp case of eval-expression, letting env6 = (extend-env '(y) (list 7) env3) } (eval-expression <<+(x, y)>> env6) = { by var-exp case of eval-expression } (eval-expression <<+(3, y)>> env6) = { by var-exp case of eval-expression } (eval-expression <<+(3, 7)>> env6) = { by primapp-exp case of eval-expression } 10