WARNING : You must move to the advanced or full language levels in
DrScheme to use the constructs presented in this lecture.

Consider an on-line address book.  The address book provides two
features: you can search it for a particular person's phone number, or
you can add a new person and phone number to the address book.  We'll
use symbols for names and numbers for phone numbers (ignore dashes).
Thus, we need to write two functions:

;; lookup-number : symbol -> (number or false)
;; returns number stored for person in phone book, or false
;;  if no number for that person in the phone book
(define (lookup-number name) ...)

;; add-to-address-book : symbol number -> true
;; updates address book with number for given name
(define (add-to-address-book name phone) ...)

These two programs need to exhibit the following behavior: after we
add a name to the address book using add-to-address-book, lookup-number
should always return a number.  So, for example, the following
sequence of calls should be correct, assuming 'Kathi was not in the
original address book:

> (lookup-number 'Kathi)
false

> (add-to-address-book 'Kathi 1234567)
true

> (lookup-number 'Kathi)
1234567

Note this is different from what we've done before.  In our previous
programs, every time you call a function on the same arguments, you
get the same answer (this is characteristic of functions in
mathematics, and a key idea underlying functional programming).
Clearly, something new has to happen to allow us to write lookup
properly.  In particular, we need to introduce variables whose value
is allowed to change.

How would you write these programs?  First, we need an address book:

(define address-book empty)

Whenever we define a variable whose value we expect to change, we
should provide a statement (like a contract) indicating its type and
what it contains.

;; An entry is a structure
;;   (make-entry Na Nu)
;; where Na is a symbol and Nu is a number
(define-struct entry (name number))

;; address-book : list[entry]
;; keep track of the current address book entries
(define address-book empty)

We call variables such as address-book state variables, because they
capture (and remember) the state of the program at some moment in
time.

Let's start by writing add-to-address-book:

;; add-to-address-book : symbol number -> true
;; updates address book with number for given name
(define (add-to-address-book name num)
  (begin
    (set! address-book
      (cons (make-entry name num) address-book))
    true))

Once we write programs that change the values of other variables, we
should document what those changes will be.  We therefore add an
effect comment to any program that changes the value of a variable.

;; add-to-address-book : symbol number -> true
;; purpose : updates address book with number for given name
;; effect : changes the value of address-book to include entry for name
(define (add-to-address-book name num)
  (begin
    (set! address-book
      (cons (make-entry name num) address-book))
    true))

This code introduces a new keyword, set!.  Set! is Scheme's assignment
operator.  You've used assignment before in other courses (as in x :=
x + 1), so the concept isn't new.  Let's look briefly at how set!
works in Scheme.  

--------------------------------------------------------------------------

A set! expression requires two pieces of information: a variable and
an expression:

	(set! var some-exp)

As with all expressions, DrScheme 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 Scheme.

This non-functioning swap program introduces another useful construct
though: local.  Local provides a means for defining local variables.
It is more powerful than the let that we have seen so far (for reasons
that aren't important at the moment).  Let's look at local more
closely.  Here's an example that uses two definitions:

(define (expt5 x)
  (local [(define (square y) (* y y))
	  (define (cube z) (* z (square z)))]
    (* (square x) (cube x))))

What happens if we put this definition into the Definitions window and
evaluate (expt5 2) in the interactions window?  We get 32, as expected.
What if we evaluate (cube 3) in the interactions window?  We get an
error.  Why?

The definitions inside of a local are only visible within the
parentheses that enclose the local.  Since the multiplication is
inside the local, it can use the functions square and cube.  Once we
pass the parenthesis that closes (local, we can no longer use those
definitions. 

We need to discuss how DrScheme evaluates a local, so that you know
what programs using local actually do.  Let's look at expt5 (the
program given above), Evaluate (+ (expt5 2) 3).  What does this become
according to our evaluation rules?

(+ (local [(define (square y) (* y y))
	   (define (cube z) (* z (square z)))]
     (* (square 2) (cube 2)))
   3)

Now, DrScheme evaluates

(local [(define (square y) (* y y))
	(define (cube z) (* z (square z)))]
  (* (square 2) (cube 2)))

At this point, DrScheme copies your local definitions as if they were
in the definitions window (you don't see them appear there, but it is
as if a little person inside the machine moved them there).  However,
in order to keep someone from using them outside of their scope,
DrScheme renames them (and changes the names in your program to
reflect the new names).  For example:

  (define (dummy-square y) (* y y))
  (define (dummy-cube z) (* z (dummy-square z)))

  (* (dummy-square 2) (dummy-cube 2)))

Note that you don't see any of these changes in the text of your
program -- this all goes on behind the scenes when DrScheme evaluates
your program.

Now, DrScheme can finish evaluating your program normally, to yield
the answer 35.  To summarize the behavior of local:

   ...top-level definitions..
   (local (defs)
    body)

becomes:
   ...top-level definitions..
   ...defs...                       (renamed for uniqueness)
   body                             (with renaming)

--------------------------------------------------------------------------

Now, we can return to our address book program.  Let's write
lookup-number.

;; lookup-number : symbol -> (number or false)
;; returns number stored for person in phone book, or false
;;  if no number for that person in the phone book
(define (lookup-number name)
  (local [(define matches
	    (filter (lambda (an-entry)
		      (symbol=? name (entry-name an-entry)))
		    address-book))]
    (cond [(empty? matches) false]
	  [else (entry-number (first matches))])))

As a reminder, here's the definition of add-to-address-book and
address-book from before:

;; An entry is a structure
;;   (make-entry Na Nu)
;; where Na is a symbol and Nu is a number
(define-struct entry (name number))

;; address-book : list[entry]
;; keep track of the current address book entries
(define address-book empty)

;; add-to-address-book : symbol number -> true
;; purpose : updates address book with number for given name
;; effect : changes the value of address-book to include entry for name
(define (add-to-address-book name num)
  (begin
    (set! address-book
      (cons (make-entry name num) address-book))
    true))

This example motivates one rule for when to use set!.  Use set! when a
program must provide more than one service, and one of those services
can change the underlying information.

Let's consider another program.  Traffic lights are controlled by
programs.  These programs must indicate the sequence of lights to
display.  We can write a simple controller for a traffic light as a
program that consumes no inputs and produces the next color for the
traffic light.  We'd expect this program to operate as follows:

> (next-light)
'green

> (next-light)
'yellow

> (next-light)
'red

Notice that this function can return different answers, even when
called the same way.  Programs that need to be able to do this also
require state.  In these programs, the value that a function returns
usually depends on some previous value that it computed.  You can
visualize this by drawing a picture of the sequencing of lights:

      red ----> green ----> yellow
       ^                       |
       |                       |
        -----------------------

The picture shows the sequence, and how the next color to produce
depends on the current color.  We could write the next-light program
as follows:

;; curr-color : one of 'red, 'green, or 'yellow
;; stores the current color of the traffic light
(define curr-color 'red)

;; next-light : -> 'red, 'green, or 'yellow
;; effect : changes curr-color to reflect the next color of the light
(define (next-light)
  (local [(define next-color
	    (cond [(symbol=? curr-color 'red) 'green]
		  [(symbol=? curr-color 'yellow) 'red]
		  [(symbol=? curr-color 'green) 'yellow]))]
    (begin
      (set! curr-color next-color)
      curr-color)))

This program uses something new: a lambda (function) with no
arguments.  Functions with no arguments are legal in Scheme, though
they don't arise often.  A function with no arguments is called a
thunk.

----------------------------------------------------------------------


Assume you've been running my address book program for several weeks
and have built up a large list of numbers.  Your roommate asks to use
your computer, and starts writing a program as follows:

(define address-book empty)

...

What's now happened to your address book?  It's gone.  Destroyed.
Since set! changes variable values, you've lost your entire address
book simply because someone else wrote a program that uses the same
variable name as your program.  In this case, the reuse was
accidental.  You could also imagine a malicious roommate who destroys
your address book on purpose (or someone changing the amount in an
account in a banking program).  Since set! is so destructive, we want
to design our programs to prevent such mistakes or abuses from
happening.  To do this, we should follow a simple rule:

	    Only set! variables declared in a local.

Let's see what happens to our programs when we follow this rule.
We'll start with the traffic light program.

;; curr-color : one of 'red, 'green, or 'yellow
;; stores the current color of the traffic light
(define curr-color 'red)

;; next-light : -> 'red, 'green, or 'yellow
;; effect : changes curr-color to reflect the next color of the light
(define (next-light)
  (local [(define next-color
	    (cond [(symbol=? curr-color 'red) 'green]
		  [(symbol=? curr-color 'yellow) 'red]
		  [(symbol=? curr-color 'green) 'yellow]))]
    (begin
      (set! curr-color next-color)
      curr-color)))

We want to bury curr-color inside of next-light.  How do we do it?  We
know we need to put curr-color in a local, so why not try:

;; next-light : -> 'red, 'green, or 'yellow
;; effect : changes curr-color to reflect the next color of the light
(define (next-light)
  ;; curr-color : one of 'red, 'green, or 'yellow
  ;; stores the current color of the traffic light
  (local [(define curr-color 'red)
	  (define next-color
	    (cond [(symbol=? curr-color 'red) 'green]
		  [(symbol=? curr-color 'yellow) 'red]
		  [(symbol=? curr-color 'green) 'yellow]))]
      (begin
	(set! curr-color next-color)
	curr-color)))

If we run this program, we see the following behavior:

> (next-light)
'green

> (next-light)
'green

> (next-light)
'green

What happened?  Remember how locals are evaluated.  Each time you call
next-light, scheme renames the local curr-color to a *new* dummy
name.  It's the new dummy name that gets updated on each set!, so
there's no persistence of curr-color across multiple calls to
next-light.