To illustrate what we mean by having a computational object with
time-varying state, let us model the situation of withdrawing money
from a bank account. We will do this using a
function
`withdraw`, which takes as argument an `amount`
to be withdrawn.
If there is enough money in the account to accommodate the withdrawal,
then `withdraw` should return the balance remaining after the
withdrawal. Otherwise, `withdraw` should return the message *
Insufficient funds.* For example, if we begin with \$100 in the
account, we should obtain the following sequence of responses using
`withdraw`:

withdraw(25); // output: 75

withdraw(25); // output: 50

withdraw(60); // output: "Insufficient funds"

withdraw(15); // output: 35

Observe that the expression `withdraw(25)`, evaluated twice,
yields different values. This is a new kind of behavior for a
function. Until now, all our
functions
could be viewed as
specifications for computing mathematical functions. A call to a
function
computed the value of the function applied to the given
arguments, and two calls to the same
function
with the
same arguments always produced the same result.[1]

So far, all our names were *constants* as declared by the keyword
`const`. Once declared, they did not change their value,
as appropriate for constants.
To implement functions like `withdraw`, we introduce a new kind
of declaration—*variable declarations* using the keyword `let`
instead of `const`.
After declaring a variable `balance`,
to indicate the balance of money in the account, we can define `withdraw`
as a function that accesses `balance`.
The `withdraw`
function
checks to see if `balance` is at least as large as the
requested `amount`. If so, `withdraw` decrements `balance` by `amount` and returns the new value of `balance`.
Otherwise, `withdraw` returns the *Insufficient funds*
message.
Here are the declarations of
`balance` and `withdraw`:

let balance = 100; function withdraw(amount) { if (balance >= amount) { balance = balance - amount; return balance; } else { return "Insufficient funds"; } }

Decrementing `balance` is accomplished by the
statement

balance = balance - amount;

Here $\textit{name}$ is a symbol and
$\textit{new-value}$ is any expression.
The assignment
changes $\textit{name}$ so that its value is the result obtained by
evaluating $\textit{new-value}$. In the case at hand,
we are changing `balance` so that its new value will be the result of
subtracting `amount` from the previous value
of `balance`.
[2]

The function `withdraw`
also uses a *sequential composition*
to cause two expressions to be evaluated
in the case where the `if` test is true: first decrementing
`balance` and then returning the value of
`balance`.
In general, executing the statement
`$\textit{stmt}_{1}$ $\textit{stmt}_{2}$`
causes the statements $\textit{stmt}_{1}$ and $\textit{stmt}_{2}$ to be evaluated in sequence.[3]

Although `withdraw` works as desired, the variable
`balance` presents a problem. As specified above, `balance`
is a name defined in the program environment and is freely accessible
to be examined or modified by any
function. It would be much better
if we could somehow make `balance` internal to `withdraw`, so
that `withdraw` would be the only
function
that could access `balance` directly and any other
function
could access `balance`
only indirectly (through calls to `withdraw`). This would more
accurately model the notion that `balance` is a local state
variable used by `withdraw` to keep track of the state of the
account.

We can make `balance` internal to `withdraw` by rewriting the definition as follows:

function make_withdraw() { let balance = 100; return amount => { if (balance >= amount) { balance = balance - amount; return balance; } else { return "insufficient funds"; } }; } const new_withdraw = make_withdraw();

What we have done here is use `let`
to establish an environment
with a local variable `balance`, bound to the initial
value 100.
Within this local environment, we use function definition[4] to create a
function that takes `amount` as an argument and behaves
like our previous `withdraw`
function. This
function—returned as the
result of evaluating the body of the `make_withdraw`
function—behaves in precisely
the same way as
`withdraw` but whose
variable `balance` is not accessible by any other
function.[5]

Combining
assignment statements with variable statements
is the general programming
technique we will use for constructing computational objects with
local state. Unfortunately, using this technique raises a serious
problem: When we first introduced
functions,
we also introduced the substitution model of evaluation
(section 1.1.5) to provide an interpretation of
what
function
application means. We said that applying a
function
should be interpreted as evaluating the body of the
function
with the
formal parameters replaced by their values. The trouble is that, as
soon as we introduce assignment into our language, substitution is no
longer an adequate model of
function
application. (We will see why
this is so in section 3.1.3.) As a
consequence, we technically have at this point no way to understand
why the `new_withdraw`
function
behaves as claimed above. In
order to really understand a
function
such as `new_withdraw`, we
will need to develop a new model of
function
application. In
section 3.2 we will introduce such a model,
together with an explanation of
assignment statements and variable statements.
First, however, we examine some variations on the theme established by
`make_withdraw`.

The following
function, `make_withdraw_with_balance`,
abstracts the initial balance into a parameter.
The formal parameter `balance` in
`make_withdraw_with_balance`
specifies the initial amount of money in the
account.[6]

function make_withdraw_with_balance(balance) { return amount => { if (balance >= amount) { balance = balance - amount; return balance; } else { return "insufficient funds"; } }; }

The function `make_withdraw_with_balance` can be used as follows to create two objects
`w1`
and
`w2`:

const w1 = make_withdraw_with_balance(100); const w2 = make_withdraw_with_balance(100);

w1(50); // output: 50

w2(70); // output: 30

w2(40); // output: "Insufficient funds"

w1(40); // output: 10

Observe that
`w1`
and
`w2`
are completely independent objects,
each with its own local state variable `balance`. Withdrawals
from one do not affect the other.

We can also create objects that handle deposits as well as
withdrawals, and thus we can represent simple bank accounts. Here is
a
function
that returns a bank-account object

with
a specified initial balance:

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; return balance; } function dispatch(m) { if (m === "withdraw") { return withdraw; } else if (m === "deposit") { return deposit; } else { return "Unknown request - - MAKE-ACCOUNT"; } } return dispatch; }

Each call to `make_account` sets up an environment with a local
state variable `balance`.
Within this environment, `make_account` defines
functions
`deposit` and `withdraw`
that access `balance` and an additional
function
`dispatch`
that takes a message

as input and returns one of the two local
functions.
The `dispatch`
function
itself is returned as the
value that represents the bank-account object.
This is precisely the
*message-passing*
style of programming that we saw in section 2.4.3, although
here we are using it in conjunction with the ability to modify local
variables.

`make_account` can be used as follows:

const acc = make_account(100);

(acc("withdraw"))(50);

(acc("withdraw"))(60);

(acc("deposit"))(40);

(acc("withdraw"))(60);

Each call to `acc` returns the locally defined `deposit` or
`withdraw`
function, which is then applied to the specified `amount`. As was the case with `make_withdraw`, another call to `make_account`

const acc2 = make_account(100);

// make_accumulator to be written by students const a = make_accumulator(5);

a(10); // output: 15

a(10); // output: 25

function make_accumulator(current) { function add(arg) { current = current + arg; return current; } return add; }

// make_monitored function to be written by students const s = make_monitored(math_sqrt);

s(100);

s("how many calls?"); // returns 1

const s = make_monitored(math_sqrt); s(100); display(s("how many calls")); s(5); display(s("how many calls"));

function make_monitored(f) { let counter = 0; //initialized to 0 function mf(cmd) { if (cmd === "how many calls") { return counter; } else if (cmd === "reset count") { counter = 0; return counter; } else { counter = counter + 1; return f(cmd); } } return mf; }

// make_account function to be written by students const acc = make_account(100, "secret password");

(acc("secret password", "withdraw"))(40); // result: 60

(acc("some other password", "deposit"))(40); // result: incorrect password

function make_account(balance, p) { function withdraw(amount) { if (balance >= amount) { balance = balance - amount; return balance; } else { return "Insufficient funds"; } } function deposit(amount) { balance = balance + amount; return balance; } function dispatch(m, q) { if (p === q) { if (m === "withdraw") { return withdraw; } else if (m === "deposit") { return deposit; } else { return "Unknown request - make_account"; } } else { return q => "Incorrect Password"; } } return dispatch; } const a = make_account(100, "eva"); (a("withdraw", "eva"))(50); //withdraws 50 (a("withdraw", "ben"))(40); //incorrect password

function make_account(balance, p) { let invalid_attempts = 0; //initializes to 0 function withdraw(amount) { if (balance >= amount) { balance = balance - amount; return balance; } else { return "Insufficient funds"; } } function deposit(amount) { balance = balance + amount; return balance; } function call_the_cops() { return "calling the cops as you have exceeded " + "the max no of failed attempts"; } function dispatch(m, q) { if (invalid_attempts <= 7) { if (p === q) { if (m === "withdraw") { return withdraw; } else if (m === "deposit") { return deposit; } else { return "Unknown request - make_account"; } } else { invalid_attempts = invalid_attempts + 1; return "Incorrect Password"; } } else { return call_the_cops(); } } return dispatch; }

[1]
Actually,
this is not quite true. One exception was the
random-number generator
in section 1.2.6. Another exception involved the
operation/type tables we introduced in section 2.4.3,
where the values of two calls to `get` with the same arguments
depended on intervening calls to `put`.
On the other hand, until we introduce
assignment, we have no way to create such
functions
ourselves.

[2]
Note that assignment statements look similar to and should not be confused
with constant and variable declarations of the form
`const $\textit{name}$ = $\textit{value}$;`
and
`let $\textit{name}$ = $\textit{value}$;`
in which a newly declared $\textit{name}$
is associated with a $\textit{value}$. Also similar in looks but not
in meaning are expressions of the form
`$\textit{expression}_1$ === $\textit{expression}_2$`
which evaluate to `true`
if $\textit{expression}_1$ evaluates to the same value as
$\textit{expression}_2$ and to
`false`, otherwise.

[3]
We have already used
sequential composition implicitly in our
programs, because in JavaScript the body of a
function can be a sequence
of statements, not just a single
`return` statement,
as discussed in
section 1.1.8.

[5]
In programming-language jargon, the variable
`balance` is said to be
*encapsulated* within the `new_withdraw`
function. Encapsulation reflects the general system-design principle known as the
*hiding principle*: One can
make a system more modular and robust by protecting parts of the
system from each other; that is, by providing information access only
to those parts of the system that have a

need to know.

3.1.1 Local State Variables