recitation 17: more object-oriented programming
========================================================================

* live lecture tomorrow (Thursday April 8th)

* quiz Tuesday April 13th, covers material through today
  (but not Thursday's lecture)

* let us know ASAP if you need to take a makeup exam.

========================================================================
object-oriented review questions

* what's the difference between an instance and a class?

* what's the difference between a class inheritance hierarchy &
  environment diagram?

* what's the convention for the object procedures create-foo and
  make-foo?  why do some objects only have a make-foo procedure?

* what's the difference between is-a & has-a relationships?

* what 3 features should be present in the code of a class foo that
  inherits from another class bar?

* what's the difference between (ask self message) and 
  (ask foo-part message)?  in what cases do you have to use 
  self and in what cases do you have to use foo-part?

* brainstorm a new set of classes that make use of multiple
  inheritance.  do any methods share the same name?  how will you
  resolve these conflicts?

========================================================================
finishing up the example from friday: 

* in what cases do you have to use self and in what cases do you have
  to use foo-part?

;; container
(define (make-container self)
  (let ((root-part (make-root-object self))
        (things '())) 
    (lambda (message)
      (case message
        ((TYPE) (lambda () (type-extend 'container root-part)))
        ((THINGS) (lambda () things))
        ((HAVE-THING?) (lambda (thing) (not (null? (memq thing things)))))
        ((ADD-THING) (lambda (new-thing)
		       (if (not (ask self 'HAVE-THING? new-thing))
			   (set! things (cons new-thing things))) 'DONE))
        ((DEL-THING) (lambda (thing) (set! things (delq thing things)) 'DONE))
	((FIRST) (lambda () (if (null? things) #f (car (reverse things)))))
        (else (get-method message root-part))))))

;; person
(define (create-person name)
  (create-instance make-person name))
(define (make-person self name)
  (let ((container-part    (make-container self))
	(named-object-part (make-named-object self name 'smith)))
    (lambda (message)
      (case message
        ((TYPE) (lambda () (type-extend 'person named-object-part container-part)))
	((SAY) (lambda (stuff) (for-each display stuff) (newline) 'said))
	((TAKE) (lambda (thing)
		  (cond ((ask self 'HAVE-THING? thing) 
			 (ask self 'SAY (list "I already have " (ask thing 'NAME))))
			((ask thing 'IS-A 'PERSON)
			 (ask self 'SAY (list "I cannot take " (ask thing 'NAME))))
			(else
			 ;; ************************************************
			 ;; does it matter if we ask self or container-part?
			 (ask self 'ADD-THING thing)
			 ;; ************************************************
			 (ask self 'SAY (list "I take " (ask thing 'NAME)))))))
	((DROP) (lambda (thing)
		  (display "I drop ")
		  (display (ask thing 'NAME))
		  (newline)
		  (ask self 'DEL-THING thing)))
        ((FIRST) (lambda () 
		   ;; ************************************************
		   ;; does it matter if we ask self or container-part?
		   (ask container-part 'FIRST)
		   ;; ************************************************
		   ))
        ((FIRST-THING) (lambda () (ask named-object-part 'FIRST)))
	(else (get-method message container-part named-object-part))))))

;; person-with-small-bag
(define (create-person-with-small-bag name max-things)
  (create-instance make-person-with-small-bag name max-things))
(define (make-person-with-small-bag self name max-things)
  (let ((person-part (make-person self name)))
    (lambda (message)
      (case message
        ((TYPE) (lambda () (type-extend 'person-with-small-bag person-part)))
        ((ADD-THING) (lambda (new-thing)
		       (if (> (length (ask self 'THINGS)) (- max-things 1))
			   (ask self 'DROP (ask self 'FIRST-THING))
			   #t)
		       (ask person-part 'ADD-THING new-thing)))
	(else (get-method message person-part))))))

* IN GENERAL:  It is best to use (ask self _______) instead of 
  (ask foo-part ________), unless you have a good reason to ask the part.

========================================================================
modified from fall 04 & spring 2003 quiz 2:

;; a THING is an object with a name, a weight, and a volume.
(define (create-thing name weight volume)
  (create-instance make-thing name weight volume))
(define (make-thing self name weight volume)
  (let ((root-part (make-root-object self)))
    (lambda (message) 
      (case message 
	((TYPE) (lambda () (type-extend 'thing root-part)))
	((NAME) (lambda () name)) 
	((WEIGHT) (lambda () weight)) 
	((VOLUME) (lambda () volume)) 
	((DENSITY) (lambda () (/ weight volume))) 
	(else (get-method message root-part))))))

;; a CONTAINER is a set of things.  
(define (create-container)
  (create-instance make-container))
(define (make-container self) 
  (let ((root-part (make-root-object self))
	(things '())) 
    (lambda (message) 
      (case message 
	((TYPE) (lambda () (type-extend 'container root-part)))
	((THINGS) (lambda () things)) 
	((ADD-THING) (lambda (thing) 
		       (set! things (cons thing things)) 
		       (map (lambda (thing) (ask thing 'NAME)) things)))
	((WEIGHT) (lambda () 
		    (fold-right + 0 
				(map (lambda (thing) (ask thing 'WEIGHT))
				     things)))) 
	((VOLUME) (lambda () 
		    (fold-right + 0
				(map (lambda (thing) (ask thing 'VOLUME))
				     things)))) 
	(else (get-method message root-part))))))

;; a CRATE is a hard-sided wooden crate. its weight varies with its
;; contents, but its volume is always the same.  
(define (create-crate name weight-when-empty volume)
  (create-instance make-crate name weight-when-empty volume))
(define (make-crate self name weight-when-empty volume)
  (let ((thing-part (make-thing self name weight-when-empty volume))
	(container-part (make-container self))) 
    (lambda (message) 
      (case message 
        ((TYPE) (lambda () (type-extend 'crate thing-part container-part)))
	YOUR-CODE-HERE
	(else (get-method message container-part thing part))))))

;; a BAG is a flexible cloth bag. Both its weight and its volume
;; depend on its contents.
(define (create-bag name)
  (create-instance make-bag name))
(define (make-bag self name) 
  (let ((thing-part (make-thing self name 0 0))
	(container-part (make-container self))) 
    (lambda (message) 
      (case message 
        ((TYPE) (lambda () (type-extend 'bag thing-part container-part)))
	YOUR-CODE-HERE
	(else (get-method message container-part thing part))))))

Finish the definitions above.  You can make other changes to the code
if you provide proper justification.

(define armoire (create-thing 'old-armoire 200 16))
(define bear (create-thing 'teddy-bear 1 0.5))
(define crate (create-crate 'crate 40 20))
(define bag (create-bag 'bag))

* (ask crate 'WEIGHT)            =>
* (ask crate 'ADD-THING armoire) =>
* (ask crate 'WEIGHT)            =>
* (ask crate 'VOLUME)            =>
* (ask crate 'DENSITY)           =>

* (ask bag 'WEIGHT)              => 
* (ask bag 'ADD-THING bear)      => 
* (ask bag 'WEIGHT)              => 
* (ask bag 'VOLUME)              => 

========================================================================
modified from spring 2000 quiz II

;; an appliance has an ON/OFF switch, you can query the state of the switch
;; and the appliance can blow its fuse (turning the machine off)
(define my-toaster (create-appliance)) 
(ask my-toaster 'ON?) ==> #f 
(ask my-toaster 'SWITCH 'ON) 
(ask my-toaster 'ON?) ==> #t 
(ask my-toaster 'BLOW-FUSE) 
Bang! Fuse Blown! 
(ask my-toaster 'ON?) ==> #f 
  
(define (create-appliance)
  (create-instance make-appliance))
(define (make-appliance self) 
  (let ((root-part (make-root self))
	YOUR-CODE-HERE )
    (lambda (message) 
      (case message 
	((TYPE)      (lambda () (type-extend 'appliance root-part)))
	((SWITCH)    YOUR-CODE HERE )
	((ON?) 	     YOUR-CODE HERE )
	((BLOW-FUSE) YOUR-CODE HERE )
	(else (get-method message root-part)))))

;; a blender is an appliance with a speed setting
(define my-blender (create-blender)) 
(ask my-blender 'SET-SPEED 5) 
(ask my-blender 'SPEED) ==> 5 
(ask my-blender 'ON?) ==> #F 

(define (create-blender)
  (create-instance make-blender))
(define (make-blender self) 
  (let ((appliance (make-appliance self))
	YOUR-CODE-HERE) 
    (lambda (message) 
      (case message 
	((TYPE)      (lambda () (type-extend 'blender appliance-part)))
	((SET-SPEED) YOUR-CODE-HERE)
	((SPEED)     YOUR-CODE-HERE)
	(else (get-method message appliance-part)))))) 

;; a tv is an appliance with a channel
(define (create-TV)
  (create-instance make-TV))
(define (make-TV self) 
  (let ((appliance (make-appliance self))
	YOUR-CODE-HERE)
    (lambda (message) 
      (case message 
	((TYPE)        (lambda () (type-extend 'tv appliance-part)))
	((SET-CHANNEL) YOUR-CODE-HERE)
	((CHANNEL)     YOUR-CODE-HERE)
	(else (get-method message appliance)))))) 

;; a BL-TV is both a blender AND a TV! 
;; One problem with this appliance: whenever the speed of the blender
;; goes above 3, and the TV part and blender part are both on, the
;; fuses in both parts blow.
(define (create-BL-TV)
  (create-instance make-BL-TV))
(define (make-BL-TV self) 
  (let ((BL (make-blender self)) 
	(TV (make-tv self))) 
    (lambda (message) 
      (case message 
	((TYPE)             (lambda () (type-extend 'tv appliance-part)))
	((BLOW-IF-OVERLOAD) YOUR-CODE-HERE)  ;; internal method
	((SWITCH)           YOUR-CODE-HERE)
	((SET-SPEED)        YOUR-CODE-HERE)
	(else (get-method message TV BL))))))

========================================================================