Consider the following class for making circles: ;; make-circle : number -> (circle-interface -> value) ;; consumes radius and returns a circle object (define make-circle-obj (lambda (initrad) (local [(define radius initrad) (define (area) (* radius radius pi)) (define (resize newrad) (set! radius newrad))] (lambda (service) (cond [(symbol=? service 'area) area] [(symbol=? service 'resize) resize]))))) [Note: to reduce confusion between objects and structures, we're going to use a naming convention in which classes have the form "make-*-obj".] Assume you want to enhance your circle object with more services. Specifically, you want to add a perimeter service that computes the perimeter of the circle. However, you want to do this under two restrictions: - we want to reuse to original code for make-circle-obj. - we don't want to edit the original code for make-circle-obj. This isn't an arbitrary restriction. If we can augment the functionality of existing code without modifying it, then we don't risk breaking other (existing) code that uses the original code. Let's start by writing an object with the perimeter service, then think about how to have it reuse the services from make-circle-obj. (define make-circle-perim-obj (lambda (...) (local [(define (perimeter) (* 2 pi radius))] (lambda (service) (cond [(symbol=? service 'perimeter) perimeter]))))) Clearly, there is a problem here because perimeter needs radius and radius is in circle-objects. So, we need (a) a circle object, and (b) a way to get the radius out of a circle object. Let's augment circle-objects to address the latter problem. (define make-circle-obj (lambda (initrad) (local [(define radius initrad) (define (area) (* radius radius pi)) (define (resize newrad) (set! radius newrad))] (lambda (service) (cond [(symbol=? service 'area) area] [(symbol=? service 'resize) resize] [(symbol=? service 'get-radius) radius]))))) Now, we need a circle objected that the perimeter object can use for shared services. Let's add one to the local. The name "superobj" arises from object-oriented programming practice, where the object whose services another object reuses is called "super". (define make-circle-perim-obj (lambda (...) (local [(define superobj (make-circle-obj ...)) (define (perimeter) (* 2 pi radius))] (lambda (service) (cond [(symbol=? service 'perimeter) perimeter]))))) Now, we can correct the use of radius in the perimeter function: (define make-circle-perim-obj (lambda (...) (local [(define superobj (make-circle-obj ...)) (define (perimeter) (* 2 pi (superobj 'get-radius)))] (lambda (service) (cond [(symbol=? service 'perimeter) perimeter]))))) Notice, though, that the make-circle-obj needs an initial radius. So, make-circle-perim-obj also needs initrad as an input. (define make-circle-perim-obj (lambda (initrad) (local [(define superobj (make-circle-obj initrad)) (define (perimeter) (* 2 pi (superobj 'get-radius)))] (lambda (service) (cond [(symbol=? service 'perimeter) perimeter]))))) We can use this new class definition as follows: > (define cp1 (make-circle-perim-obj 2)) > ((cp1 'perimeter)) #i12.566370614359172 What if we wanted to get the area of cp1? Right now, we'd get a "no matching cond clause" error. We could add new cases to the service cond for each service we want to reuse from make-circle-obj, but a better solution is to simply pass any remaining service requests directly to the superobj, without looking at what they are: (define make-circle-perim-obj (lambda (initrad) (local [(define superobj (make-circle-obj initrad)) (define (perimeter) (* 2 pi (superobj 'get-radius)))] (lambda (service) (cond [(symbol=? service 'perimeter) perimeter] [else (superobj service)]))))) Now, a circle-perim-obj has all the functionality of a circle-obj, plus the perimeter service. This situation, in which one object reuses all the services of another, is called "inheritance". The circle-perim class inherits services from the circle class. Here's one more inheritence example. What if we wanted a new circle class that had a center coordinate and a move service, as well as the area and resize services (but not perimeter). That would look like: (define make-circle-center-obj (lambda (initrad initx inity) (local [(define superobj (make-circle-obj initrad)) (define center (make-posn initx inity))] (lambda (service) (cond [(symbol=? service 'get-x) (posn-x center)] [(symbol=? service 'get-y) (posn-y center)] [(symbol=? service 'move) (lambda (newx newy) (set! center (make-posn newx newy)))] [else (superobj service)]))))) ;> (define c6 (make-circle-center-obj 2 10 20)) ;> (c6 'get-x) ;10 ;> (c6 'get-y) ;20 ;> ((c6 'move) 25 35) ;> (c6 'get-x) ;25 ;> (c6 'get-y) ;35 Note the difference between this new class and circle-perim is that circle-center needs three initialization parameters, but only the initrad passes along to make-circle-obj. We could draw the relationships between our three circle classes as follows: make-circle-obj ^ ^ | | | | make-circle-perim-obj make-circle-center-obj where the arrows indicate that the class at the start of the arrow shares the services of the class at the end of the arrow. This is the standard picture of inheritence between classes. Note that make-circle-obj does not have the perimeter or move services.