Memory is implemented with variable definitions. The memory-using programs we have seen use a single variable to represent the memory of a function. In principle, a single variable is enough to implement all memory needs, but this is usually inconvenient. Typically, the memory analysis suggests how many variables we need and which services need which variables. When memory changes, the corresponding variables assume a new value or, put differently, the state of the variable declaration changes and reflects the memory change over time. We therefore refer to variables that implement memory as STATE VARIABLES. Every service in a program corresponds to a function that may employ auxiliary functions. A service that changes the memory of a program is implemented with a function that uses set! on some of the state variables. To understand how a function should change a state variable, we need to know what kind of values the variable may represent and what its purpose is. In other words, we must develop a contract and a purpose statement for state variables in the same manner in which we develop contracts and purpose statements for function definitions.
Let us take a look at the address-book and the traffic-light examples. The first one has one state variable: address-book. It is intended to represent a list of entries, where each entry is a list of two items: a name and a number. To document that address-book may represent only such lists, we add a contract as follows:
;; address-book : (listof (list symbol number)) ;; to keep track of pairs of names and phone numbers (define address-book empty)By the definition of (listof X), it is permissible to use empty as the initial value of address-book.
From the contract for the state variable, we can conclude that the following assignment is nonsensical:
(set! address-book 5)It sets address-book to 5, which is not a list. The expression therefore violates the state variable's contract. But
(set! address-book empty)is proper, because it sets address-book back to its initial value. Here is a third assignment:
(set! address-book (cons (list 'Adam 1) address-book))It helps us gain some understanding of how functions can change the value of address-book in a useful manner. Because address-book stands for a list of lists, (cons (list 'Adam 1) address-book) constructs a longer list of the right kind. Hence the set! expression just changes the state variable to stand for a different value in the class of (listof (list symbol number)).
A program that controls a traffic light should have a state variable for the current color of the traffic light. This variable should assume one of three values: 'red, 'green, or 'yellow, which suggests a data definition:
A TL-color is either 'green, 'yellow, or 'red.
Here is the variable definition with a contract and purpose statement:
;; current-color : TL-color ;; to keep track of the current color of the traffic light (define current-color 'red)As before, the expression
(set! current-color 5)is nonsensical because 5 is not one of the three legitimate symbols mentioned in the contract. In contrast,
(set! current-color 'green)is perfectly okay.
The right-hand side of an assignment does not have to consist of a value or an expression that almost instantaneously produces a value. In many cases it makes sense to use a function to compute the new value. Here is a function that computes the next color for our traffic light:
;; next-color : TL-color -> TL-color
;; to compute the next color for a traffic light
(define (next-color c)
(cond
[(symbol=? 'red c) 'green]
[(symbol=? 'green c) 'yellow]
[(symbol=? 'yellow c) 'red]))
Using this function, we can now write an assignment that switches the state
of current-color appropriately:
(set! current-color (next-color current-color))Because current-color is one of the three legitimate symbols, we can apply next-color to the value of current-color. The function also produces one of these three symbols, so that the next state of current-color is again proper.