[3/31/99] =============================================================================== defclass and call-next-method So far the only way we have to create a new type in Swindle is with defstruct. While this is helpful, we would like a more flexible way of defining types. What are some of the limitations of defstruct? It only allows single inheritance, and it creates functions that we may not need. Also, we have a lot more options that we can use with the following defclass form. First we look at the syntax of a new special form in Swindle, that of defclass. Show the syntax first, then explain the meaning. -------------------- (defclass (...) ; empty list means inherits from (slot option...) ... (slot option...)) -------------------- This essentially creates a new type. Some of the options you can use are :type - this is constrains the values that can be in this slot :initarg - when making a slot, use this to give the slot an initial value :initvalue - if we don't specify a value in make, this is the default :initializer - a function that takes a value and returns an init value :reader - reads the contents of a slot :writer - overwrites the contents of a slot (essentially set!) :accessor - This gives you both a reader and a writer Say you did this (foo :accessor foo), you get the following: - a reader such that (foo x) returns the contents of the slot foo, if x is an instance of a class that has this slot - a writer (set-foo! x val) which overwrites the slot foo in x with val - a possibility to use set! so (set! (foo x) val) will do the same thing as (set-foo! x val) Also should note that defining accessor with reader/writer is redundant. We will show how defstruct can be written in terms of a defclass. -------------------- (defstruct color (x )) -------------------- || || \/ -------------------- (defclass () ; defstruct always inherits from (color :reader get-point-color :accessor point-color :initarg :color) (x :type :reader get-point-x :accessor point-x :initarg :x)) (define (make-point color x) (make :color color :x x)) (define (point? p) (instance-of? p )) (defmethod (equals? (x ) (x )) (and (equals? (point-x x) (point-x y)) (equals? (point-y x) (point-y y)))) -------------------- Okay, now that we're a little familiar with defclass, lets jump into a good example. -------------------- (defclass () (name :type :initarg :name :reader name)) (defclass () (fun :type :initarg :fun :accessor fun :initvalue 0)) (defclass () (size :type :initarg :size :accessor size :initvalue 30)) (defclass ( ) (owner :type :initarg :owner :reader owner :writer new-owner!)) (defclass () (meanness :type :initvalue 1000 :accessor mean)) -------------------- Should say at this point that it is extremely important to think before writing. Right here, we should stop and draw a class hierarchy. These can be very useful when implementing anything in an object-oriented system. More importantly, we would can see the line of inheritance. -------------------- (define fido (make :name 'fido :fun 500 :owner "CS 212")) (define lassie (make :name 'lassie :fun 250 :owner "Timmy")) (define steve (make :name 'steve :size 140)) -------------------- When we use defclass, we are creating the definition of the class. The special form make, as just used, creates what are called instances of a class. -------------------- (defgeneric (like x y)) ; returns whether x likes y (defmethod (like (x ) (y )) "Not friends") (defmethod (like (x ) (y )) (if (< (fun y) (mean x)) (call-next-method) "Could live together")) (defmethod (like (x ) (y )) "Look - a mailman!! Bark, bark!!") (defmethod (like (x ) (y = fido)) (set! (mean x) (- (mean x) 300)) "We're pals!! Want a bone") -------------------- Two things are very important here. The notion of call-next-method and singleton "classes". * A generic function holds a list of methods (defmethod creates a method and adds it to the list in the generic). When you apply a generic function it will invoke the most specific method that it contains (in case of conflicts the arguments on the left are more important). Inside the method body you can call the argument-less function call-next method, and the next specific method will be invoked (with the same arguments). * Also, singleton class represent a class that holds exactly one object. For example, (singleton 1) is the "class" that holds only 1. This allows you to create a generic function that specializes on a specific object. In the definitions above, we use a more convenient syntax (y = fido) which is equivalent to (y (singleton fido)). ===============================================================================