Comp210 Lecture # 29    Fall 2002

Changing the World: mutating structures

A structure, for today's examples:

(define-struct cat (name age high?))
;; a cat is:
;;  (make-cat <symbol> <num> <boolean>)
;; where age is in cat-years (about 12/7 months),
;; and high? indicates whether the cat is high on catnip.

; Examples:
(define hyde (make-cat 'bartok 21 true))
(define cat2 (make-cat 'freddy-kruger 83 false))

;; sneak: cat --> cat
;; Returns a new cat, which is like a-cat, except that
;; it is exposed to catnip (regardless of whether a-cat was).
;;
(define (sneak a-cat)
  (make-cat (cat-name a-cat) (cat-age a-cat) true))

(sneak hyde) = hyde
(sneak cat2) = (make-cat 'freddy-kruger 83 true)

The way the world works is not that sneaking into the toy closet destroys the old cat and creates a new cat with many similar attributes. (This is perhaps a very Zen way of looking at things: "the cat you once had is no more; there is only the cat of the current moment, playfully ripping apart your ankles.")

Really, we think of it being the same cat, only that it has been modified over time. This has been a major exception to our motto "your program mirrors your thinking of the problem". We now introduce a way to really model this after all (but our world is going to get more complicated; we deferred until now to explore the richness of the more limited world, before moving on):

hyde
(set-cat-high?! hyde true)
hyde
(set-cat-name!  hyde 'Pumar-Lord-Of-The-Urban-Jungle)
hyde
Write a function grow-cat!, which takes in a cat, and returns the same cat one (cat)year older.
Go back and modify your function so that after growing, the kitty is no longer high.
Write grow-cats!, which takes in a list of cats.

If Elizabeth Windsor dyes her hair, what happens to the queen of england? She has purple hair too!
(define hyde   (make-cat 'bartok 21 true))
(define jekyll hyde)
(set-cat-age! hyde 22)
jekyll ; A cat w/ age 22.
jekyll and hyde are two different placeholders that represent the same underlying structure. (One student that changing jekyll wouldn't change hyde, because of the order defined, but that isn't so:)
(set-cat-age! jekyll 23)
hyde   ; A cat w/ age 23.
Both placeholders really do both refer to the same. What is the relation between hyde and some other cat named 'bartok, 22 cat-years, who is high -- is that the same underlying cat, or is it an entirely different cat that just happens to look alike on the outside? (Hopefully the latter, since that better reflects the real world.)

How, exactly, does this work? The law-of-scheme as we know it doesn't explain this. Let's attempt the law-of-scheme as it stands. Think about what happened at each step. From the top:

  ; An incorrect hand-evaluation:

  (define hyde   (make-cat 'bartok 21 true))
  (define jekyll hyde) 
  ; The placeholder hyde gets replaced with its defined value:
= (define jekyll (make-cat 'bartok 21 true))

  (set-cat-age! hyde 22)
= (set-cat-age! (make-cat 'bartok 21 true) 22)
= ; ?? Nothing returned; we expected?:  (make-cat 'bartok 22 true)

  jekyll ; Law of scheme for placheholder says 
         ; to look up its define'd value, which was (make-cat 'bartok 21 true)
= (make-cat 'bartok 22 true)   ; Uh-oh, not its defined value!


  ; Furthermore, we have another mystery:
  (set-cat-age! jekyll 11)
= (set-cat-age! (make-cat 'bartok 22 true) 11)  ; Evaluate placeholder "jekyll"
= ; ?? Nothing returned; we expected?:  (make-cat 'bartok 11 true)

  hyde ; Again, last we saw, hyde stood for a cat of age 10, but:
= (make-cat 'bartok 11 true)    mysterious!
Clearly the law of scheme as we understood it, doesn't account for what we're seeing. What exactly is the mechanism, so that both refer to the same underlying cat?

Here is the truth: Whenever you call make-cat (or any make-[struct] -- including cons, or (make-, build-)vector) what gets returned is not a box with those entries, but actually a reference to a box with those entries. A reference is a new type of value. We'll indicate references with arrows (on the chalk board), or with hat-variables (in text):

  (define hyde (make-cat 'bartok 10 true))
= (define hyde hyde^)
where hyde^ is a value; that is hyde^ is a valid thing for a function to return. make-cat actually did two things: it (a) created a cat (a box with three drawers named "age", ...), (b) returned a reference to that box. We'll draw this on the board as:
             Cat:
             +==================+
             # name:  'bartok   #
             #        ~~~~~~~~  #
             # age:     22      #
             #        ~~~~~~~   #
             # high?:   true    #
             #        ~~~~~~~   #
             +==================+
                ^
                |
(define hyde ---/ )
Well, it gets unwieldy to draw those boxes, so we'll write the box as (make-cat 'bartok 22 true), as ever. And rather than repeatedly drawing arrows which point off to the box, we'll write (define hyde^ (make-cat 'bartok 22 true)). Thus we'll write the entire above picture as:
(define hyde^ (make-cat 'bartok 22 true))   ; hyde^ is a signpost to this make-cat
(define hyde hyde^)  ; The value of hyde is a signpost to that make-cat.
In a nutshell: Reference values are an abstract concept; to depict them we'll choose either of: This mechanism now explains the behavior we saw earlier, where hyde and jekyll were clearly the same underlying cat (we just had two different wasys of referring to it).

Example: a hand-evaluation

  (define liz (make-person 'elizabeth-windsor 'grey))
  (define queen liz)
  (set-person-haircolor! liz 'blue)
  (person-haircolor queen)
When we evaluated the following in lecture, we had a series of pictures (about six of them -- but i kept drawing on top of the previous picture). Here is the full hand-evaluation, where each "picture" is instead a list of define's and expressions. We won't harp on these full hand-evaluations (which now consist not of a single expression, but a list of several defines/expressions), but you should be clear on what's happening, and how it still follows the law-of-scheme.
We highlight the (sub)expression about to be evaluated next.
  ; We're about to start evaluating all this:
  (define liz (make-person 'elizabeth-windsor 'grey))
  (define queen liz)
  (set-person-haircolor! liz 'blue)
  (person-haircolor queen)

= (define liz^ (make-person 'elizabeth-windsor 'grey))
  (define liz liz^)
    ; We just evaluated the first "define liz..." above.
  (define queen liz)
  (set-person-haircolor! liz 'blue)
  (person-haircolor queen)


= (define liz^ (make-person 'elizabeth-windsor 'grey))
  (define liz liz^)
  (define queen liz^)
    ; It was easy to define queen -- just look up value of placeholder "liz",
    ; which is liz^.
  (set-person-haircolor! liz 'blue)
  (person-haircolor queen)

= (define liz^ (make-person 'elizabeth-windsor 'grey))
  (define liz liz^)
  (define queen liz^)
  (set-person-haircolor! liz^ 'blue) ; Again look up value of "liz", which is
                                     ; the reference "liz^"
  (person-haircolor queen)

= (define liz^ (make-person 'elizabeth-windsor 'blue))
  (define liz liz^)
  (define queen liz^)
  ; set-person-haircolor! returned nothing, but note the above side effect.
  (person-haircolor queen)

= (define liz^ (make-person 'elizabeth-windsor 'blue))
  (define liz liz^)
  (define queen liz^)
  (person-haircolor liz^)  ; Evaluating placeholder "queen" easily gave "liz^"

= (define liz^ (make-person 'elizabeth-windsor 'blue))
  (define liz liz^)
  (define queen liz^)
  'blue   ; Easy to evaluate person-haircolor -- just follow the
          ; reference, and peek inside that structure.
Note that each step was very small -- just evaluating a placeholder (which means finding what it was defined as, same as it always has been), or making/setting/accessing a structure.

This bit about using references to structures, rather than immediate structures, may seem like a bit of superficial syntactic wordplay, but it makes a huge difference. What if we'd said:?

  (define liz (make-person ...))
  (define queen-of-england (make-person ...))
  (set-person-haircolor! liz 'purple)
Note that references are created whenever you make a structure, even if you don't associate a placeholder with it. In fact, it's an easy observation: there is exactly one unique structure created, for every call to make-structure.

Here is another (slightly shorter) review of law-of-scheme, aimed at labbies.

 

©2002 Ian Barland