Notes on Set!

We've seen that set! is the assignment operator in Racket. These notes give you an overview of what Racket will and will not let you do with set!, and what we (the graders) will and will not let you do with set!.

A set! expression requires two pieces of information: a variable and an expression: (set! var some-exp) As with all expressions, DrRacket evaluates some-exp first, then changes the value of var to the value of some-exp. A set! expression has (returns) an invisible value. However, it has a visible effect because the old definition of var gets erased and replaced with the value of some-exp. We can see this by hand-evaluating the following expression: (define n 5) (begin (set! n (+ n 1)) n) Obviously, this should (and does) return 6. If we try to hand-evaluate it to see why, we get (define n 6) (begin n) which returns 6. So, set! effectively replaces an old define with a new one. Begin evaluates each expression in turn (until none are left), throwing away each expression after it evaluates it. Let's look at two more example programs: (define x 3) (define y 4) (begin (set! x y) (set! y x)) This becomes (define x 4) (define y 4) (begin (set! y x)) which in turn becomes (define x 4) (define y 4) Notice that, contrary to possible expectations, the original code does not swap the values of x and y. This is because expressions within a begin are evaluated in order. The effect of each set! within a begin occurs before the next one is evaluated. Let's try writing a swap program that works. (define u 4) (define v 5) (define (swap x y) (local [(define tmp x)] (set! x y) (set! y tmp))) (swap u v) What happens if I check the values of u and v after the call to swap? They still have their original values. This happens because set! on a parameter to a function does not affect anything outside of the function. As of now, you don't have the ability to write a swap function in Racket.

Set! can only be used on identifiers (global variables, parameters, or variables defined with let or other forms that introduce identifiers).

Set! cannot be used to change a value within a list or a structure.

If you use set! on a parameter of a function, the change is not visible outside of the function. The only way to get a change to be visible outside of a function is to use set! on a global variable.

As a general rule of thumb, you should only use set! if you have two functions accessing a variable where one changes the value of the variable and the other accesses the value of the variable. You should not use set! to implement things like loop counters, to accumulate values locally within a function, etc. While your code will run if you do this, it's not functional programming. Basically, if you find yourself trying to write C++ code in Racket, you may be overstepping the bounds of using set!. Ask one of us for help if you're unsure whether you can use set! for a particular purpose.