Our address book program from the last class consisted of a variable
address-book and two functions lookup-number and add-to-address-book.
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.

This point arose briefly in our memoization lecture: we want to hide
variables inside the functions that use them.  In that lecture, we
mentioned it as an issue of good style.  In reality, it's an issue of
security as well as good style.  Today, we're going to discuss issues
that arise when we follow this rule.

Let's start with our traffic light program from the last class:

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

We want to bury curr-color inside of next-light.  How do we do it?  As
in the memoization example, we introduce a local before the lambda:

;; 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)]
     (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 works, as we said earlier, because the lambda expression traps
the dummy name for curr-color.  Each reference to curr-color inside
the lambda expression therefore refers to a persistent variable.

Now, let's consider our address book program.  Hiding the variable
here is a little harder than for traffic light because now there are
two functions that refer to address-book, not just one.  What options
do we have?

1. Put a copy of address-book inside each function.  That won't work
because then each function would be accessing its own private copy,
rather than sharing a single copy, as is needed.

2. Put the two functions that use address book as local functions
inside of one wrapper function.

Let's explore the second approach.  What might such a function look
like?

(define (address-interface ...)
  (local [(define address-book empty)
	  (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))])))
	  (define (add-to-address-book name num)
	    (begin
	      (set! address-book
		(cons (make-entry name num) address-book))
	      true))]
    ...))

The problem here is that we no longer have a way to use our original
programs, lookup-number and add-to-address-book.  We need some way to
get those functions out of address-interface once they've captured the
dummy name for address-book.  How could we do this?

Our first temptation might be to return a list containing the
functions, as follows:

(define (address-interface)
  (local [(define address-book empty)
	  (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))])))
	  (define (add-to-address-book name num)
	    (begin
	      (set! address-book
		(cons (make-entry name num) address-book))
	      true))]
    (list lookup-number add-to-address-book)))

This solution is bad for three reasons:

1. It doesn't scale.  If I added 16 new features to my program, my
return list gets very complicated.  How will I remember which function
is in which position in the list?

2. It's not flexible.  I could write an address book program with tons
of features.  A user who only wants some of those features should have
to know about all the unnecessary features.

3. The return type is arbitrary.  We made a list because we didn't
know what else to do.  However, a list of programs isn't a natural
return value for a program that should be returning one address book
program.

We really want to get back one program through which we can access all
of the services that we want to use.  We need a way to tell the
program which service we want.  Let's assign a symbolic name to each
service in our address book.  We'll use 'lookup for lookup-number and
'add for add-to-address-book.  Now, our address book program can
consume one of these names and give us back the program that we want:

;; An address-book-service is one of
;;  - symbol -> number
;;  - symbol number -> true

;; address-interface : symbol -> address-book-service
;; returns the program for the requested service
(define address-interface
  (local [(define address-book empty)
	  (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))])))
	  (define (add-to-address-book name num)
	    (begin
	      (set! address-book
		(cons (make-entry name num) address-book))
	      true))]
    (lambda (service)
      (cond [(symbol=? service 'lookup) lookup-number]
            [(symbol=? service 'add) add-to-address-book]))))

In this situation, we should not use else for the 'add service,
because there is a good chance that we'll want to add other services
later on.

How do we use our new program?  There are two ways.  We could use
defines to give names to the functions that we plan to use:

> (define lookup-address (address-interface 'lookup))
> (define add-address (address-interface 'add))
> (lookup-address 'Kathi)
false
> (add-address 'Kathi 3)
true
> (lookup-address 'Kathi)
3

The other way is to not define the names, but to query the
address-interface for the functions when we want to use them:

> ((address-interface 'lookup) 'Kathi)
false
> ((address-interface 'add) 'Kathi 3)
true
> ((address-interface 'lookup) 'Kathi)
3

The first looks better because we don't keep querying for the function
over and over.  However, there are cases where the second is more
appropriate (we'll see one later in the lecture or in next class, time
depending).

How hard is it to add a new service to the address book?  Not hard at
all.  We add the program for the new service to the local and add a
cond clause to return the new service.  As an example, let's add an
update service.  Update should consume a name and a number and replace
the person's entry in the phone book with the new entry.  Writing an
update function is easy (straightforward structural recursion).

;; address-interface : symbol -> address-book-service
;; returns the program for the requested service
(define address-interface
  (local [(define address-book empty)
	  (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))])))
	  (define (add-to-address-book name num)
	    (begin
	      (set! address-book
		(cons (make-entry name num) address-book))
	      true))
	  (define (update-address name num)
	    (local [(define updated-book
		      (map (lambda (entry)
			     (cond [(symbol=? (entry-name entry) name)
				    (make-entry name num)]
				   [else entry]))
			   address-book))]
	      (set! address-book updated-book)))]
    (lambda (service)
      (cond [(symbol=? service 'lookup) lookup-number]
	    [(symbol=? service 'add) add-to-address-book]
	    [(symbol=? service 'update) update-address]))))

> ((address-interface 'lookup) 'Kathi)
3
> ((address-interface 'update) 'Kathi 5)
> ((address-interface 'lookup) 'Kathi)
5

Now that your new address book program is finished, you and your
roommate decide that you each want your own address book.  You have
your program [address-interface].  How do you give your roommate an
address book that won't conflict with yours?  One option is to copy
the code and change the function name to address-interface2, but that
copies a lot of code.  Avoid copying code when possible.  If you add a
new feature to (or correct a mistake in) your address book program,
you don't want to have to make that same edit to every copy of the
program that you've handed out.

We're really like a function that returns a new address book program
each time that we call it.  We could define such a program as follows:

;; make-address-book : -> (symbol -> address-book-service)
;; returns a new address book program
(define (make-address-book)
  (local [(define address-book empty)
	    (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))])))
	    (define (add-to-address-book name num)
	      (begin
		(set! address-book
		  (cons (make-entry name num) address-book))
		true))
	    (define (update-address name num)
	      (local [(define updated-book
			(map (lambda (entry)
			       (cond [(symbol=? (entry-name entry) name)
				      (make-entry name num)]
				     [else entry]))
			     address-book))]
		     (set! address-book updated-book)))]
    (lambda (service)
	(cond [(symbol=? service 'lookup) lookup-number]
	      [(symbol=? service 'add) add-to-address-book]
	      [(symbol=? service 'update) update-address]))))

> (define Kathi-book (make-address-book))
> (define John-book (make-address-book))
> ((Kathi-book 'add) 'Tom 4)
true
> ((Kathi-book 'lookup) 'Tom)
4
> ((John-book 'lookup) 'Tom)
false

This completes our example of writing programs that provide multiple
services with memory.  We've looked at how to hide variables when
multiple programs need to access them and how to use symbolic messages
to tell a program which service we want to provide.

As a side note about programming: we've just taught you how to design
objects (as in object-oriented programming).  An object is simply a
closure with multiple entry points, each entry point defined by a
unique message.  This course has taught you two of the three
fundamental concepts behind objects: data-driven processing (ie,
templates) and encapsulated services (today's example).  To get
object-oriented programming, all you need is inheritence (don't worry
if you don't know what that is -- this is for the people who do).  You
could add inheritence to this program without about 15 or so lines of
code.