Last class, we saw two new Scheme keywords: begin and set!.  Let's
briefly review each:

Begin takes any number of expressions larger than one.  It evaluates
each of its n expressions in order, throws away the results of the
first n-1, and returns the last as the value of the expression.
Therefore:

(begin 
  (cons 4 (list 5))
  7)

reduces to

(begin 
  7)

which yields 7.

Thus, it's clear that begin is only useful if its first n-1
expressions change the values of some variables.

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 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)

This becomes

(define n 6)

(begin n)

which returns 6.

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.

Today, we want to look more closely at programs that require memory
about previous computations.

Up until now, all of the functions that we've written have returned
the same value, regardless of when we called them.  Once we add memory
to programs, this feature may no longer hold.

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 address-book -> (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

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 : (listof 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.

Writing lookup is easy:

;; 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))])))

What about 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))

Notice that in this case the purpose and effect are basically the
same.  This is because add-to-address-book doesn't do anything other
than change the value of address-book.  In such cases, it's okay to
leave off the purpose statement and just include an effect statement.
For programs that change the value of a variable and compute a value,
you should have both a purpose and an effect statement.

Does our method of designing test cases need to change once we're
writing programs with state?  Yes, because we need to make sure that
the values of variables are being changed as we intended.  The format
of our examples also has a change a bit, because the program may use
variables that are not provided as inputs.  Our add-to-address-book
program, for example, relies on address-book, but doesn't consume it.
Here's are samples of our new examples:

;; if address-book is empty and we evaluate (add-to-address-book 'Sam 1),
;; then address-book is (list (make-entry 'Sam 1)) afterwards

;; if address-book is (list (make-entry 'Ella 3)) and we evaluate
;; (add-to-address-book 'Sam 1), then address-book is
;; (list (make-entry 'Sam 1) (make-entry 'Ella 3)) afterwards.

;; if address-book is (list e1 ... en) and we evaluate
;; (add-to-address-book 'Sam 1), then address-book is
;; (list (make-entry 'Sam 1) e1 ... en) afterwards.

Note that our examples cover several possible values of address book,
as our examples have done in the past.

This discussion has demonstrated some new features to the design
recipe when writing programs that involve state:

- Add contracts to state variables
- Add effect statements to programs that change memory
  (sometimes, no purpose statement because only has effect)
- Examples/test cases must illustrate effects 
- Put a set! statement on the appropriate variable in the template

Let's look at our address book program all together now.

;; 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 : (listof entry)
;; keep track of the current address book entries
(define address-book empty)

;; 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))])))

;; 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
  (lambda ()
    (local [(define next-color
	      (cond [(symbol=? curr-color 'red) 'green]
		    [(symbol=? curr-color 'yellow) 'red]
		    [(symbol=? curr-color 'green) 'yellow]))]
      (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.