Now functions run. Today you evaluate a call - make a fresh environment, bind each argument to its parameter, evaluate the body in that environment, and return the result.
Evaluate a function call by binding arguments to parameters in a new enclosed environment and running the body.
Calling a function has four steps: evaluate the argument expressions to values,
make a new environment for this call, bind each parameter name to its argument
value there, and evaluate the body in that environment. The result of the body is
the call’s value, so add(2, 3) gives 5.
The new environment must enclose the function’s captured environment: name
lookup checks the local (call) scope first, then falls back outward. Extend
Environment.Get to consult an outer environment when a name is not found
locally. This nesting is what gives functions local scope - a parameter x
shadows an outer x for the duration of the call - and it is the exact mechanism
closures will exploit. Arguments being evaluated first is why double(double(3))
is 12: the inner call runs before the outer one.
// an environment can enclose an outer one for name lookupfunc NewEnclosedEnvironment(outer *Environment) *Environment { ... }// apply: env := NewEnclosedEnvironment(fn.Env)// bind each evaluated arg to fn.Parameters[i], then Eval(fn.Body, env)// Get falls back to the outer environment when a name is not local