; More examples. ; Before defining everything we want to do with foods, we'll need to define ; some more basic verbs and nouns: pots, pans, burners, etc. Let's assume ; this stuff is in another file someplace. Note how we isolate this lower ; level of abstraction from the higher level we're interested in now. (load "kitchen-primitives") ; First we'll define a bunch of kinds of food. (define (make-named-class 'food (list ) (list 'calories 'cost))) ; That is, is a class of objects, and every instance of ; has two data slots: calories and cost. (define (make-named-class 'cooked-food (list ) (list 'cooking-time))) ; That is, is a subclass of . Every instance of ; has three data slots: cooking-time, calories, and ; cost; note that the last two are inherited from its superclass. (define (make-named-class 'hamburger (list ) (list))) ; is a subclass of with no data slots except ; the three it inherits from its superclass: cooking-time, calories, ; and cost. (define (make-named-class 'ice-cream (list ) (list 'flavor))) (define (make-named-class 'rice (list ) (list))) (define (make-named-class 'brown-rice (list ) (list))) (define (make-named-class 'white-rice (list ) (list))) (define (make-named-class 'fruit (list ) (list))) (define (make-named-class 'fruit-with-peel (list ) (list))) (define (make-named-class 'apple (list ) (list))) (define (make-named-class 'banana (list ) (list))) (define (make-named-class 'water (list ) (list))) ; not exactly, but we do ingest it. (define (make-named-class 'hamburger-bun (list ) (list))) ; Note that a can be thought of BOTH as a ; (you can eat it), AND as a (you can put something into ; it). This is a phenomenon called "multiple inheritance"; we'll ; talk about it later in the semester. ; Now we define the "verbs" that we're likely to apply to these foods. (define eat (make-generic)) ; Takes a and returns nothing. (define prepare (make-generic)) ; Takes a and returns a that is ready to eat. (define toppings (make-generic)) ; Takes a and returns a list of possible toppings. ; Now we define how those verbs are carried out on different kinds of foods. (add-method prepare (make-method (list ) (lambda (compute-next-method food-item) food-item))) ; By default, you don't do anything to prepare a food item; ; you just return it. (add-method prepare (make-method (list ) (lambda (compute-next-method food-item) (error "This is an abstract method.")))) ; For cooked foods, we know we need to cook the thing, but we don't ; know how. If anybody tries to prepare a cooked-food without saying ; more specifically what kind of cooked-food it is, print an error ; message. (add-method prepare (make-method (list ) (lambda (compute-next-method hamburger) (let ((pan (get-thing )) (bun (get-thing ))) ; In "kitchen-primitives" is a function "get-thing" which, when you ; tell it what class of thing you need, finds one and returns it. ; The "let" gives us local variables named "pan" and "bun", ; initialized by getting a and a ; respectively. (set-burner 'medium) (put-on-burner pan) (wait '(30 seconds)) (put-in hamburger pan) (wait (slot-ref hamburger 'cooking-time)) (turn-over hamburger) (wait (slot-ref hamburger 'cooking-time)) (set-burner 'off) (take-out hamburger pan) (put-in hamburger bun) bun)))) ; when we're finished, we return the bun, which contains the hamburger, ; so somebody can eat it. (add-method prepare (make-method (list ) (lambda (compute-next-method rice) (let ( (pan (get-thing )) (water (get-thing )) (bowl (get-thing ))) (set-burner 'high) (put-on-burner pan) (put-in water pan) (wait-until (lambda () (boiling? water))) (put-in rice pan) (set-burner 'low) (wait (slot-ref rice 'cooking-time)) (set-burner 'off) (take-out rice pan) (put-in rice bowl) rice)))) (add-method prepare (make-method (list ) (lambda (compute-next-method rice) (wash rice) (compute-next-method)))) ; To prepare brown rice, use the usual method for preparing rice, but ; wash the rice first. (Note how this avoids duplicating code.) (add-method prepare (make-method (list ) (lambda (compute-next-method fruit) (take-off-peel fruit)))) (add-method toppings (make-method (list ) (lambda (compute-next-method hamburger) (list 'ketchup 'mustard 'pickles 'onions)))) (add-method toppings (make-method (list ) (lambda (compute-next-method ice-cream) (list 'chocolate-sauce 'sprinkles 'peanuts)))) (add-method toppings (make-method (list ) (lambda (compute-next-method rice) (list 'vegetables 'sauce)))) (add-method initialize (make-method (list ) (lambda (compute-next-method rice initargs) (slot-set! rice 'cooking-time '(35 minutes))))) ; "initialize" is a predefined generic that takes two arguments: the ; object to initialize and any initialization arguments provided with ; it. We shall ignore the initialization arguments and just set the ; cooking time. (add-method initialize (make-method (list ) (lambda (compute-next-method rice initargs) (slot-set! rice 'cooking-time ('20 minutes))))) (define wash (lambda (thing-to-wash) (let ( (bowl (get-thing )) (water (get-thing ))) (put-in thing-to-wash bowl) (put-in water bowl) (take-out water bowl) (take-out thing-to-wash bowl) thing-to-wash))) ; Note that I've defined "wash" as an ordinary Scheme function, ; rather than as a method for a generic. If it turned out that I ; needed to wash different things in different ways, I would ; probably change my mind. But any existing code that called ; "wash" wouldn't need to be changed! (define take-off-peel (make-generic)) (add-method take-off-peel (make-method (list ) (lambda (cnm fruit) (print "Peeling a " (class-name-of fruit) "."))))