Simply Scheme Chapter 7 – Variables

Quotes from here or elsewhere in Simply Scheme unless otherwise noted.

References to “SS” or “the book” are to Simply Scheme.

Variables

In functional programming, variable means something like a named constant in algebra (e.g. when x^3 – 8 = 0, x = 2). Formal parameters in a procedure definition aren’t variables but are mere names. However, when you invoke a procedure with a particular argument, the formal parameter/name is associated with a particular value, and so a variable is created. Later, if the procedure is invoked again, a new variable might be created with a different value.

Part of what functional programming means is that you don’t change the value of a variable once it exists. This contrasts with languages where you do change the value of the same variable after bringing it into existence (as opposed to creating a new variable). “Programs in those languages tend to be full of things like ‘X = X + 1.'”

One issue to keep in mind is you can have more than one variable with the same name at the same time, because you can invoke a procedure that in turn invokes a procedure and both of them use the same formal parameter name.

There might be one variable named x with the value 7, and another variable named x with the value 51, at the same time. The pitfall to avoid is thinking “x has changed its value from 7 to 51.”

These x’s are different variables.

As an analogy, imagine that you are at a party along with Mick Jagger, Mick Wilson, Mick Avory, and Mick Dolenz. If you’re having a conversation with one of them, the name “Mick” means a particular person to you. If you notice someone else talking with a different Mick, you wouldn’t think “Mick has become a different person.” Instead, you’d think “there are several people here all with the name Mick.”

Little People and Variables

Little people model can help understand how variables work.

In the above set of expressions, one little person (who the book calls Hortense) thinks x is 3 for the purposes of evaluating hypotenuse. Hortense hires Solomon to compute (square 3) and Solmon happens to associate x with 3, which is the same association between a formal parameter and a name that Hortense had in hypotenuse. But these associations represent different variables (and this is presumably reflected in the computer’s memory somewhere). When Hortense hires Sheba to compute square 4, Sheba associates x with 4, but again this is a different x than the x that Hortense associated with 3 (and for that matter, a different x than the x that Solomon associated with 3, despite the same procedure being invoked for both of them).

(Remember that we said a variable is a connection between a name and a value. So x isn’t a variable! The association of the name x with the value 5 is a variable. The reason we’re being so fussy about this terminology is that it helps clarify the case in which several variables have the same name. But in practice people are generally sloppy about this fine point; we can usually get away with saying “x is a variable” when we mean “there is some variable whose name is x.”)

The book also mentions that procedures invoked by other procedures don’t have awareness of the meaning of variables that the parent procedure is using, so you have to explicitly tell the procedure being invoked what’s a particular value is if you want that procedure to use it. So for example this doesn’t work:

the g procedure has no knowledge of the meaning of x to the f procedure that’s calling g. You can tell the g procedure the value explicitly though:

Global and Local Variables

You can use define to permanently associate a name with a value and not just for defining a procedure. This creates a global variable – an association between a name and a value that applies generally and not just for the purposes of evaluating a procedure. All the “little people” are aware of such a global variable, unlike in the case of local variables, where the association between a formal parameter and an actual argument is local to a procedure.

Chalkboards and Truth About Substitution

SS gives a chalkboard analogy for understanding variables. Global variables are up on a big chalkboard that all little people can see. What about local variables? Each little person evaluating a procedure has their own chalkboard that they work on to handle variables for that particular procedure.

SS also says the substitution model discussed earlier isn’t actually accurate. Instead of making a new copy of an expression with the appropriate values substituted in for the formal parameters, what Scheme actually does is use the original expression and look up the value of each name when it’s needed, maintaining variables on several “chalkboards” in order to be able to have local variables.

Let

let lets you basically create a temporary procedure without getting it a name and lets you invoke that procedure with a specified argument value.

Let is a special form that takes two arguments. The first is a sequence of name-value pairs enclosed in parentheses. […] The second argument, the body of the let, is the expression to evaluate.

Pitfalls

The book notes that if you’ve programmed in other languages before, you may be used to changing the value of a variable by assigning it a new value and thus want to write something like:

Definitions are meant to be permanent in functional programming.

When you create more than one temporary variable at once using let, all of the expressions that provide the values are computed before any of the variables are created. Therefore, you can’t have one expression depend on another:

“all of the expressions that provide the values are computed before any of the variables are created” means that, for example, (+ 4 7) and (* a 5) are calculated before the results of those expressions are associated with a and b and so on. No associations between names and values are made until all such calculations are done within the let. So if Scheme tries to evaluate the above, when it gets to the (* a 5) it has nothing to put in for the a.

Don’t think that a gets the value 11 and therefore b gets the value 55. That let expression is equivalent to defining a helper procedure

and then invoking it:

The argument expressions, as always, are evaluated before the function is invoked. The expression (* a 5) will be evaluated using the global value of a, if there is one. If not, an error will result. If you want to use a in computing b, you must say

This last one works because the value of the a in the first let is computed and then the a variable is created. Having been created, a is available for use as a value to provide to b within the second let.

Exercises

Boring Exercises

✅ Exercise 7.1

The following procedure does some redundant computation.

Use let to avoid the redundant work.

my solution:

AnneB did this a bit differently and just figured out the article in the let, whereas I slapped the whole article + world combination in. Hers is probably better cuz I’m not sure how logical my grouping of article and word together is.

✅ Exercise 7.2

Put in the missing parentheses:

Answer:

🍰

Real Exercises

✅ Exercise 7.3

The following program doesn’t work. Why not? Fix it.

It doesn’t work cuz it uses word as both a procedure name and a variable name.

It’s supposed to work like this:

Fixed:

✅ Exercise 7.4

What does this procedure do? Explain how it manages to work.

The let in this procedure causes the addition procedure + to act as the multiplication procedure * and the multiplication procedure * to act as the addition procedure + within the body of the let.

A correctly written sum of squares procedure should square its arguments and then add them. In the body of the procedure, we see that we instead have arguments each being added to themselves once and then multiplied. However, the definitions in the let will be evaluated before the body runs. Therefore, the redefinitions take effect, and the procedure actually works as intended. E.g. (+ a a) becomes (* a a) and likewise (+ b b) becomes (* b b). Multiplying a number by itself is squaring it. Then what was the * becomes a + and so the result is:

This is a correct sum of squares procedure, and so the procedure produces the correct output, contrary to what you might initially expect.