5.92

20 Circular Data

Books & Authors

Books have: title : String author : Author

Authors have: name : String books : [Listof Book]

As data def:
;; A Book is (book% String Author)
;; An Author is (author% String [Listof Book])

Can we make an Author?
(author% "Rose" empty)
(book% "Reign of Roquet" (author% "Rose" empty))

But this is wrong: the Rose has written a book, but the author object doesn’t know about it.

Do we need books to know the author? Yes.

We’ve seen this before with graph structure. We represented graphs as association lists, using symbolic names.
;; A Book is (book% String Author)
(define-class book%
  (fields title author))
 
;; An Author is (author% String [Listof Book])
(define-class author%
  (fields name books))
 
(define rose  (author% "Rose" empty))
(define reign (book% "Reign of Roquet" rose))
But:
reign
rose

Question: Does reign contain a copy of rose, or are they all the same rose? Answer: always the same, because we use the name rose, we didn’t construct a new one.

Let’s add a new method for modifying the author after a book is written:
(define (add-book b)
  (set-field! books (cons b (this . books))))

Now we change our example:
(define rose  (author% "Rose" empty))
(define reign (book% "Reign of Roquet" rose))
(rose . add-book reign)

How does it print?

> rose

#0=(object:author% "Rose" (list (object:book% "Reign of Roquet" #0#)))

> reign

#0=(object:book% "Reign of Roquet" (object:author% "Rose" '(#0#)))

See graph-style printing.

But every times we construct a book with an author, we want to use add-book. So, let’s use the constructor.

(constructor (t a)
  (fields t a)
  (send a add-book this))

In the first expression, we cannot use this, and we must produce the result using fields. Later, we can use this, and we get the desired result.