define-struct is DrScheme's mechanism for creating records (new forms
of compound data).  Whenever you create a new form of compound data,
you should write down three things:

1. A comment (the data definition or data model) that indicates 
   the types of each field of the compound data

2. The define-struct call that creates your new form of data

3. Examples of data created with the new definition

Here are these three items for our boa definition from class

  ;; A boa is a 
  ;;   (make-boa symbol number symbol)

  (define-struct boa (name length food))

  (make-boa 'Slinky 15 'pets)
  (make-boa 'Slim 10 'lettuce)

define-struct creates a number of functions for you.  For the boa
case, DrScheme creates the following functions:

  make-boa : symbol number symbol -> boa    (constructor)
  boa-name : boa -> symbol                  (selectors)
  boa-length : boa -> number
  boa-food : boa -> symbol
  boa? : value -> boolean                   (recognizer)

Here's another example

  ;; An armadillo is a
  ;;   (make-armadillo number boolean)

  (define-struct armadillo (length dead?))

  (make-armadillo 3 true)
  (make-armadillo 6 false)

This defines the following functions:

  make-armadillo : number symbol -> armadillo
  armadillo-length : armadillo -> number
  armadillo-dead? : armadillo -> boolean
  armadillo? : value -> boolean

We can write programs over these new kinds of data as we've done over
numbers and symbols:

  ;; run-over : armadillo -> armadillo
  ;; return a dead armadillo that is one unit longer than the input armadillo
  (define (run-over adillo)
    (make-armadillo (+ (armadillo-length adillo) 1)

Sometimes, we want to write programs that process a collection of
types of data.  For example, we might want to write programs to manage
a zoo of both boas and armadillos.  Consider a program longer-than?,
which checks whether a boa or armadillo is longer than a given length.
What would the contract look like?  Here's one possibility:

  ;; longer-than? : (boa or armadillo) number -> boolean

Is this contract good or bad?  What happens if we later add tigers to
our zoo?  Then giraffes?  And so on.  For flexibility, we really want
a single name by which we can refer to the collection of animals in
our zoo.  So, we want something like

  ;; longer-than? : animal number -> boolean

This is more flexible (and cleaner), but we haven't defined what an
animal is.

  ;; An animal is either
  ;;   - a boa, or
  ;;   - an armadillo

Notice that this data definition doesn't require a define-struct
because we are not trying to build a new kind of data.  We are merely
introducing a name by which we can refer to several variants of a kind
of data (this is analagous to creating a class animal with subclasses
boa and armadillo -- you expect to make boas and armadillos, but never
generic animals).

Why is this more flexible than listing the types of animals in the
contract though?  Wouldn't we have to add tiger, giraffe, etc to our
animal definition just as we'd have to add them to our longer-than?
contract?  [left as an exercise to the reader]

How do we write programs over animals?  Let's fill in a bit more of
longer-than? (the ... are the pieces we still need to fill in):

  ;; longer-than? : animal number -> boolean
  ;; determine whether an animal is longer than a given length
  (define (longer-than? ani len)
    (cond [(boa? ani) ...]
          [(armadillo? ani) ...]))

We know that the input is an animal.  Our data definition for animal
tells us that an animal is one of two types of data.  So, we need a
cond to tell us which type of animal we have.  

We can proceed to fill in the rest of the program:

  ;; longer-than? : animal number -> boolean
  ;; determine whether an animal is longer than a given length
  (define (longer-than? ani len)
    (cond [(boa? ani) (> (boa-length ani) len)]
          [(armadillo? ani) (> (armadillo-length ani) len)]))

Notice there is no animal-length operation, because we never wrote
(define-struct animal ...).  If you want an animal-length function,
you have to define it from scratch.