9.2 Impact of mutation

Introducing assignment presents many complications for our programming model. It invalidates the substitution model of evaluation introduced in Section 3.6.2 and found satisfactory until this point. All the procedures we can define without using mutation behave almost like mathematical functions---every time they are applied to the same inputs they produce the same output.1Assignments allow us to define non-functional procedures that produce different results for different applications even with the same inputs.

Example 9.1: Counter

Consider the update-counter! procedure:

(define (update-counter!)
  (set! counter (+ counter 1))

To use update-counter!, we must first define the counter variable it uses:

(define counter 0)

Every time (update-counter!) is evaluated the value associated with the name counter is increased by one and the result is the new value of counter. Because of the hidden begin expression in the definition, the (set! counter (+ counter 1)) is always evaluated first, followed by counter which is the last expression in the begin expression so its value is the value of the procedure. Thus, the value of (update-counter!) is 1 the first time it is evaluated, 2 the second time, and so on.

The substitution model of evaluation doesn't make any sense for this evaluation: the value of counter changes during the course of the evaluation. Even though (update-counter!) is the same expression, every time it is evaluated it evaluates to a different value.

Mutation also means some expressions have undetermined values. Consider evaluating the expression (+ counter (update-counter!)). The evaluation rule for the application expression does not specify the order in which the operand subexpressions are evaluated. But, the value of the name expression counter depends on whether it is evaluated before or after the application of update-counter! is evaluated!

The meaning of the expression is ambiguous since it depends on the order in which the subexpressions are evaluated. If the second subexpression, counter, is evaluated before the third subexpression, (update-counter!), the value of the expression is 1 the first time it is evaluated, and 3 the second time it is evaluated. Alternately, but still following the evaluation rules correctly, the third subexpression could be evaluated before the second subexpression. With this ordering, the value of the expression is 2 the first time it is evaluated, and 4 the second time it is evaluated.

  1. Observant readers should notice that we have already used a few procedures that are not functions including the printing procedures from Section 4.5.1, and random and read-char from the previous chapter.