Section 1.1.8 introduced the idea that functions can have internal definitions, thus leading to a block structure as in the following function to compute square roots:

function sqrt(x) { function good_enough(guess) { return abs(square(guess) - x) < 0.001; } function improve(guess) { return average(guess,x/guess); } function sqrt_iter(guess){ return good_enough(guess) ? guess : sqrt_iter(improve(guess)); } return sqrt_iter(1.0); }

Now we can use the environment model to see why these internal
definitions behave as desired. Figure 3.11 shows the point in the
evaluation of the expression `sqrt(2)` where the internal
function
`good_enough`
has been called for the first time with
`guess` equal to 1.

Observe the structure of the environment. `sqrt` is a symbol in
the program environment that is bound to a
function
object whose
associated environment is the program environment. When `sqrt` was
called, a new environment E1 was formed, subordinate to the program
environment, in which the parameter `x` is bound to 2. The body
of `sqrt` was then evaluated in E1. Since the first expression in
the body of `sqrt` is

function good_enough(guess) { return abs(square(guess) - x) < 0.001; }

After the local
functions
were defined, the
expression `sqrt_iter(1.0)` was evaluated,
still in environment E1. So the
function
object bound to
`sqrt_iter`
in E1 was called with 1 as
an argument. This created an environment E2 in which `guess`,
the parameter of
`sqrt_iter`,
is bound to 1.
The function `sqrt_iter` in
turn called
`good_enough`
with the value of `guess` (from
E2) as the argument for
`good_enough`.
This set up another
environment, E3, in which `guess` (the parameter of
`good_enough`)
is bound to 1.
Although
`sqrt_iter`
and
`good_enough`
both have a parameter named `guess`, these are two
distinct local variables located in different frames. Also, E2 and E3
both have E1 as their enclosing environment, because the
`sqrt_iter`
and
`good_enough`
functions
both have E1 as their
environment part. One consequence of this is that the symbol `x`
that appears in the body of
`good_enough`
will reference the
binding of `x` that appears in E1, namely the value of `x`
with which the original `sqrt`
function
was called.

The environment model thus explains the two key properties that make local function definitions a useful technique for modularizing programs:

- The names of the local functions do not interfere with names external to the enclosing function, because the local function names will be bound in the frame that the function creates when it is run, rather than being bound in the program environment.
- The local functions can access the arguments of the enclosing function, simply by using parameter names as free variables. This is because the body of the local function is evaluated in an environment that is subordinate to the evaluation environment for the enclosing function.

function make_account(balance) { function withdraw(amount) { if (balance >= amount) { balance = balance - amount; return balance; } else { return "Insufficient funds"; } } function deposit(amount) { balance = balance + amount; } function dispatch(m) { return m === "withdraw" ? withdraw : m === "deposit" ? deposit : "Unknown request: make_account"; } return dispatch; }

const acc = make_account(50); (acc("deposit"))(40); (acc("withdraw"))(60);

const acc2 = make_account(100);

There is currently no solution available for this exercise. This textbook adaptation is a community effort. Do consider contributing by providing a solution for this exercise, using a Pull Request in Github.

3.2.4
Internal Definitions