(make-me thods 工s-A (lambda (type) (memg type (ask self TYPE)))))) The root object provides a basis for providing common behaviors to all classes Specifically, it provides a convenient method(Is-A)to see if a type descriptor is in the TYPE list. We will by convention use this class as the root for all other classe Named-objects have no other local state than the name variable. They do have four methods: TYPE NAME. INSTALL and DESTRoY. The TYPE method comes from the make handler procedure and it indicates that named-objects have the type named-object in ddition to any type descriptors that the root-part has. The INSTALL method is not required, but if it exists, it is called when an instance is created. In the case of named object, there is nothing to do at creation time, but we'll later see examples where this method is non-trivial. The name is a selector in that it returns the name with which the object was created However, the named-object procedure only builds a handler for an object of type named-object. In order to get an instance, we need a create-named-object procedure (define (create-named-object name) symbol - named-object (create-ins tance named-object name)) Here, an instance is created using the named-object procedure. The create-instance procedure builds a handler procedure that serves as a container for the real handler for the nstance. It also attaches a tag or label to the handler, so that we know we are working with an instance. We need this extra complexity because each foo procedure expects self as an argument, so we build an instance object, then create the handler, passing the instance object in for self. You'll explore more of this system in the questions below Using instances Once you have an instance, you can call the methods on it using the ask procedure (define book (create-named-object 'sicp)) (ask book 'NAME) alue: si (ask book : Value: (named-object root
'root (make-methods 'IS-A (lambda (type) (memq type (ask self 'TYPE)))))) The root object provides a basis for providing common behaviors to all classes. Specifically, it provides a convenient method (IS-A) to see if a type descriptor is in the TYPE list. We will by convention use this class as the root for all other classes. Named-objects have no other local state than the name variable. They do have four methods: TYPE, NAME, INSTALL, and DESTROY. The TYPE method comes from the makehandler procedure and it indicates that named-objects have the type named-object in addition to any type descriptors that the root-part has. The INSTALL method is not required, but if it exists, it is called when an instance is created. In the case of namedobject, there is nothing to do at creation time, but we'll later see examples where this method is non-trivial. The NAME is a selector in that it returns the name with which the object was created. However, the named-object procedure only builds a handler for an object of type named-object. In order to get an instance, we need a create-named-object procedure: (define (create-named-object name) ; symbol -> named-object (create-instance named-object name)) Here, an instance is created using the named-object procedure. The create-instance procedure builds a handler procedure that serves as a container for the real handler for the instance. It also attaches a tag or label to the handler, so that we know we are working with an instance. We need this extra complexity because each foo procedure expects self as an argument, so we build an instance object, then create the handler, passing the instance object in for self. You'll explore more of this system in the questions below. Using Instances Once you have an instance, you can call the methods on it using the ask procedure: (define book (create-named-object 'sicp)) (ask book 'NAME) ;Value: sicp (ask book 'TYPE) ;Value: (named-object root)
The ask procedure retrieves the method of the given name from the instance, and then calls it. Retrieving a method from a handler is done with get-method, which ends up calling the handler procedure with the method name as the message. The specifics of the ask procedure and related procedures can be found in objsysscm Inheritance and subclasses We've already built a class, named-object, that inherited from its parent class, root object. If the handler for a named-object is sent a message that it doesn't recognize, it attempts to get a method from its parent (last line of make-handler procedure). Each handler creates a private handler for its parent to pass these messages to(the let statement in named-object). Because this parent handler is part of the same instance as the overall handler the self value is the same in both However, let's move on to a subclass of named-object called a thing. a thing is an object that will have a location in addition to a name. Thus, we may think of a thing as a kind of named object except that it also handles the messages that are special to things This arrangement is described in various ways in object-oriented jargon, e.g., "the thing class inherits from the named-object class "or"thing is a subclass of named-object d-object is a superclass of thing. (define (create-thing name location) ymbol, location - thing (create-instance thing name location ) (define (thing self name locatio (let ((named-part (named-object self name)) (make-methods INSTALL (lambda ( (ask named-part INSTALL) (ask (ask self loCation)' 'ADD-THING self) ' lOCATIoN (lambda ( location DESTROY (lambda o (ask (ask self 'loCATIoN) DEL-THING self) lambda (text) (ask screen TELL-ROOM (ask self 'LOCATION) (appen (list"At sk (ask self LOCATION)NAME) text))) named-part)) A very interesting(and confusing! property of object-oriented systems is that subclasses can specialize or override methods of their superclasses. In lecture, you saw this with professors sAying things differently than students. a subclass overrides a method on the superclass by supplying a method of the same name. For example, thing overrides the INSTALL method of named-object. When the user of the object tries to get the method
The ask procedure retrieves the method of the given name from the instance, and then calls it. Retrieving a method from a handler is done with get-method, which ends up calling the handler procedure with the method name as the message. The specifics of the ask procedure and related procedures can be found in objsys.scm. Inheritance and Subclasses We've already built a class, named-object, that inherited from its parent class, rootobject. If the handler for a named-object is sent a message that it doesn't recognize, it attempts to get a method from its parent (last line of make-handler procedure). Each handler creates a private handler for its parent to pass these messages to (the let statement in named-object). Because this parent handler is part of the same instance as the overall handler, the self value is the same in both. However, let's move on to a subclass of named-object called a thing. A thing is an object that will have a location in addition to a name. Thus, we may think of a thing as a kind of named object except that it also handles the messages that are special to things. This arrangement is described in various ways in object-oriented jargon, e.g., "the thing class inherits from the named-object class," or "thing is a subclass of named-object," or "named-object is a superclass of thing." (define (create-thing name location) ; symbol, location -> thing (create-instance thing name location)) (define (thing self name location) (let ((named-part (named-object self name))) (make-handler 'thing (make-methods 'INSTALL (lambda () (ask named-part 'INSTALL) (ask (ask self 'LOCATION) 'ADD-THING self)) 'LOCATION (lambda () location) 'DESTROY (lambda () (ask (ask self 'LOCATION) 'DEL-THING self)) 'EMIT (lambda (text) (ask screen 'TELL-ROOM (ask self 'LOCATION) (append (list "At" (ask (ask self 'LOCATION) 'NAME)) text)))) named-part))) A very interesting (and confusing!) property of object-oriented systems is that subclasses can specialize or override methods of their superclasses. In lecture, you saw this with professors SAYing things differently than students. A subclass overrides a method on the superclass by supplying a method of the same name. For example, thing overrides the INSTALL method of named-object. When the user of the object tries to get the method
named INSTALL, it will be found in thing and things version of the method will be eturned(because it never reaches the else clause in make-handler which checks the parent named-object-part). The thing class overrides two methods on named-object explicitly (as well as two implicitly); can you point out which two explicit methods are overridden One of the methods which thing overrides in an implicit manner is the TYPE method This is one of the methods that every class is supposed to override, as it allows the class to include its type descriptor in the list of types that the object has. This allows the class of an instance to be discovered at run time (define building (create-thing 'stata-center MIT)) (ask building TYPE) Value: (thing named-object root There is a handy method on the root-object called Is-A that uses the TYpe method to determine if an object has a certain type (ask building Is-A ' thing) (ask building ' IS-A named-object) (ask book 'Is-A ' thing) Value: #f (ask book Is-A named-object You' ll note that building is considered to be both a thing and a named-object, because even though it was built as a thing, things inherit from named-object Using superclass methods In the thing code the DESTRoY method uses(ask self lOCATION) in order to figure out where to remove itself from. However, it could have just referenced the location variable. It doesn't because one of the tenets of object-oriented programming is"if there 's a method that does what you need, use it. The idea is to re- use code as much reasonable. (It turns out just using location would be a bug in this case; you'll be able to see why after doing the warm-up exercises!) Some of the time, when you specialize a method, you want the subclass method to do something completely different than the superclass. For example, the way massive-stars DIE(supernova! )is very different than the way stars DIE(burn out). However, the rest of
named INSTALL, it will be found in thing and thing's version of the method will be returned (because it never reaches the else clause in make-handler which checks the parent named-object-part). The thing class overrides two methods on named-object explicitly (as well as two implicitly); can you point out which two explicit methods are overridden? One of the methods which thing overrides in an implicit manner is the TYPE method. This is one of the methods that every class is supposed to override, as it allows the class to include its type descriptor in the list of types that the object has. This allows the class of an instance to be discovered at run time: (define building (create-thing 'stata-center MIT)) (ask building 'TYPE) ;Value: (thing named-object root) There is a handy method on the root-object called IS-A that uses the TYPE method to determine if an object has a certain type: (ask building 'IS-A 'thing) ;Value: #t (ask building 'IS-A 'named-object) ;Value: #t (ask book 'IS-A 'thing) ;Value: #f (ask book 'IS-A 'named-object) ;Value: #t You'll note that building is considered to be both a thing and a named-object, because even though it was built as a thing, things inherit from named-object. Using superclass methods In the thing code, the DESTROY method uses (ask self 'LOCATION) in order to figure out where to remove itself from. However, it could have just referenced the location variable. It doesn't because one of the tenets of object-oriented programming is "if there's a method that does what you need, use it." The idea is to re-use code as much as is reasonable. (It turns out just using location would be a bug in this case; you'll be able to see why after doing the warm-up exercises!) Some of the time, when you specialize a method, you want the subclass' method to do something completely different than the superclass. For example, the way massive-stars DIE (supernova!) is very different than the way stars DIE (burn out). However, the rest of
the time, you may want to specify some additional behavior to the original. This presents a problem: how to call your superclass' method from within the overriding method D)will give rise to an infinite loop Thus, instead of asking ourself, we ask our superclass-part. Note that we do this with the INSTALL method of a thing, where we explicitly ask the superpart to also install, as well as doing some specific actions. This is the only situation in which you should be asking your superclass-part! Classes for a simulated worl When you read the code in objtypes. scm, you will see definitions of several different classes of objects that define a host of interesting behaviors and capabilities using the OOP style discussed in the previous section. Here we give a brief"tour" of some of the important classes in our simulated world Container Class Once we have things, it is easy to imagine that we might want containers for things. We can define a utility container class as shown below: (define (container self) (let ((root-part (root-object self)) (things ()) (make-handler container (make-methods I THINGS (lambda ( things) HAVE-THING? (lambda (thing) (not (null? (memg thing things)))) ADD-THING (lambda (thing) (if (not (ask self HAvE-THING? thing) (set! things (cons thing things))) DEL-THING (lambda (thing) (set! things (delg thing things)) DONE)) root-part))) Note that a container does not inherit from named-object, so it does not support messages such as NAME or INSTALL Containers are not meant to be stand-alone objects to create dure); rather, they only meant to be used internally by other objects to gain the capability of adding things, deleting things, and checking if one has something Place class
the time, you may want to specify some additional behavior to the original. This presents a problem: how to call your superclass' method from within the overriding method. Following the usual pattern of (ask self 'METHOD) will give rise to an infinite loop! Thus, instead of asking ourself, we ask our superclass-part. Note that we do this with the INSTALL method of a thing, where we explicitly ask the superpart to also install, as well as doing some specific actions. This is the only situation in which you should be asking your superclass-part! Classes for a Simulated World When you read the code in objtypes.scm, you will see definitions of several different classes of objects that define a host of interesting behaviors and capabilities using the OOP style discussed in the previous section. Here we give a brief "tour" of some of the important classes in our simulated world. Container Class Once we have things, it is easy to imagine that we might want containers for things. We can define a utility container class as shown below: (define (container self) (let ((root-part (root-object self)) (things '())) (make-handler 'container (make-methods 'THINGS (lambda () things) 'HAVE-THING? (lambda (thing) (not (null? (memq thing things)))) 'ADD-THING (lambda (thing) (if (not (ask self 'HAVE-THING? thing)) (set! things (cons thing things))) 'DONE) 'DEL-THING (lambda (thing) (set! things (delq thing things)) 'DONE)) root-part))) Note that a container does not inherit from named-object, so it does not support messages such as NAME or INSTALL. Containers are not meant to be stand-alone objects (there's no create-container procedure); rather, they are only meant to be used internally by other objects to gain the capability of adding things, deleting things, and checking if one has something. Place class