1.14 State
Warning: If you are using Plait with a programming-languages course, then the instructor has almost certainly disallowed the constructs in this chaper for use in your homework solutions, except as specifically allowed. Don’t use set!, begin, boxes, or vectors unless the instructor says that you can. If you’re tempted to use one of those, you’re doing it wrong.
We have so far described define as naming constants, but names bound by define are not necessarily constant. The value associated to the name can be changed using set!.
> (define gravity 6.6e-11) > gravity - Number
6.6e-11
> (set! gravity 6.5e-11) - Void
> gravity - Number
6.5e-11
The type of a set! expression is Void, meaning that it doesn’t return a useful value, and the useless value doesn’t even print as a result. If you need to change a variable and then return a value, use begin to sequence the operations. The value of a begin form is the value of its last expression.
> (define counter 0)
> (define (fresh-number!) (begin (set! counter (add1 counter)) counter)) > (fresh-number!) - Number
1
> (fresh-number!) - Number
2
The ! at the end of fresh-number! is a convention to warn readers that calling the function can have a side effect.
Although you can set a variable’s value using set!, you can’t directly pass a variable to another function that changes the variable’s value. A set! on a function’s argument would change the argument variable’s value, but would have no effect on the caller’s variables. To make a mutable location that can be passed around, Plait supports boxes. You can think of a box as a mutable object that has a single field, where box creates a fresh object, unbox extracts the object’s field, and set-box! changes the object’s field.
> (define counter1 (box 0)) > (define counter2 (box 0))
> (define (fresh-number-at! c) (begin (set-box! c (add1 (unbox c))) (unbox c))) > (fresh-number-at! counter1) - Number
1
> (fresh-number-at! counter1) - Number
2
> (fresh-number-at! counter2) - Number
1
> (fresh-number-at! counter1) - Number
3
A vector is a traditional mutable array. Every element of a vector must have the same type, which can be inferred from the value that you suply when making a vector to serve as the initial value for each of the vector’s slots. The vector function creates a vector, vector-ref accesses a slot value by position, and vector-set! changes a slot value by position.
> (define counters (make-vector 2 0))
> (define (fresh-number-at-index! i) (begin (vector-set! counters i (add1 (vector-ref counters i))) (vector-ref counters i))) > (fresh-number-at-index! 0) - Number
1
> (fresh-number-at-index! 0) - Number
2
> (fresh-number-at-index! 1) - Number
1
> (fresh-number-at-index! 0) - Number
3