4 -*- Mode:Text; Package:User; Fonts:(TR12 HL12B TR12I CPTFONT HL10 CPTFONTB) -*-* 3------------------------------------------------------------------------------* 1CLOS Tutorial* 49/1/88 original version* 4 3/1/89 latest revision* 4 Copyright (C) 1988, Texas Instruments Incorporated. All rights reserved. 1Introduction** This document is a brief tutorial introduction to the Common Lisp Object System. It introduces the features that are most likely to be needed by casual users, but does not attempt to mention all of the capabilities of CLOS. The reader is assumed to be acquainted with Lisp, but no previous knowledge of object-oriented programming is required. CLOS is an extension to Common Lisp for object-oriented programming. It provides capabilities similar to Flavors (plus additional capabilities), although the conceptual model and syntax is more like Symbolics' New Flavors or Xerox's Common LOOPS. CLOS was designed by a group of representatives from several companies, working under the Common Lisp standardization committee (committee X3J13) of the American National Standards Institute; it is intended to be included in the forthcoming ANSI standard for Common Lisp. While the description that follows conforms to the current draft specification of CLOS, it should be realized that there may be further changes to the specification before it is finally adopted as a standard. 1Loading* In order to try using the TI implementation of CLOS, you will need: * An Explorer or microExplorer. * A load band for release 4 or later. * A microcode band for release 5 or later: Exp1-ucode #573 or greater Exp2-ucode #345 or greater Mx-ucode #87 or greater Once you have an appropriate band and microcode booted, do: 3(make-system "CLOS")* The CLOS symbols will then be visible from the 3USER* package. If you create your own package, have it use the 3CLOS* package. For example: 3(defpackage 2my-pkg* (:use "LISP" "TICL" "CLOS"))* There is also a 3TICLOS *package, which exports the same symbols as 3CLOS *plus a number of TI extensions to CLOS. You may want to use the 3TICLOS* package instead of 3CLOS* if you want easy access to the extensions. Bug reports may be mailed to 3CLOS@DSG*. If you want to be notified of future updates, add yourself to the file 3"DSG:MAILING-LISTS;TICLOS-USERS.MLIST"*. 1Object terminology* An 2object* is a data structure which contains some number of 2slots*. A 2slot* is a location where a value can be stored. Each slot is identified by a name, which is a symbol. An object is an 2instance* of some 2class*, where the class defines how many slots there are, what their names are, and other information that is common to all instances of the class. 1Creating and accessing objects* Classes are defined by using the 5defclass* macro. For example: 3(defclass point () (x y))* creates a class named 3point*, each instance of which will have two slots, which are named 3x* and 3y*. (The second argument, shown here as 3()*, will be discussed later.) Objects of this class can now be created by the 5make-instance* function. For example, 3(setq object (make-instance 'point))* creates an object of class 3point*. Classes are also defined as type names, so 3(typep object 'point) => t* The slots in an object can be accessed by the 5slot-value* function. For example, 3(setf (slot-value object 'x) 13)* stores a value into slot 3x* of 3object*, and 3(print (slot-value object 'x))* displays the value. The function 5slot-boundp* can be used to test whether a slot has a value. For example, 3(slot-boundp object 'y) => nil* because we haven't yet stored any value in slot 3y*. When making several references to slots within an object, the 5with-slots* macro provides a convenient notation that avoids writing many calls to 5slot-value*. The 5with-slots* macro declares that certain names represent slots in a particular object, and then allows those names to be used as variables within the body forms. For example, 3(with-slots (x y) object* 3 (setq y (* x 2)))* declares that variables 3x* and 3y* represent the corresponding slots in 3object*. This example macro-expands into: 3(setf (slot-value object *'3y) (* (slot-value object *'3x) 2))* It is also possible for 5with-slots* to declare a variable name different from the slot name. This is important when referencing slots from more than one instance. For example: 3(with-slots ((x1 x) (y1 y)) object1* 3 (with-slots ((x2 x) (y2 y)) object2* 3 (values (- x1 x2) (- y1 y2))))* Here, variable 3x1* represents slot 3x* in 3object1*, while variable 3x2* represents slot 3x* in 3object2*. 1Slot options* Usually you will want to initialize the values of the slots when an object is created. In the 5defclass* form, instead of just a slot name, a slot can be specified by a list of the slot name followed by keyword options. The slot option 5:initarg* can be used to associate the slot with a keyword argument to 5make-instance.* For example, 3(defclass point () ((x :initarg :xval) (y :initarg :yval)))* allows us to do: 3(make-instance 'point :xval 1 :yval 2)* which creates an instance and initializes slot 3x* to 31* and slot 3y* to 32.* Another slot option is 5:initform,* which can be used to specify a default value. For example, 3(defclass point () ((x :initarg :xval :initform 0) (y :initarg :yval :initform 0)))* says to initialize the slots to 30* if the 5make-instance* argument is not specified. Thus 3(slot-value (make-instance 'point :xval 15) 'x) => 15* 3(slot-value (make-instance 'point) 'x) => 0* As its name implies, the 5:initform* can be a form instead of just a constant. The form is evaluated during 5make-instance* only if it is needed. For example, with 3(defclass choice () ((answer :initarg :answer * 3:initform (y-or-n-p "Shall I?"))))* doing 3(make-instance 'choice :answer t)* initializes the slot to 3t*, while 3(make-instance 'choice)* queries the user for the value. Rather than using the 5slot-value* function to access the slots, it is more common to define functions to access the slot values that you want to be accessible from the outside. This can conveniently be done by using the 5:accessor* slot option. For example, 3(defclass thing () ((a :initform nil :accessor thing-a)))* defines a function named 3thing-a*, such that 3(thing-a 2object*)* returns the value of slot 3a* in 2object,* and (setf (thing-a 2object*) 2new-value*) stores a new value into slot 3a*. Outside slot access can be restricted to read-only or write-only by using the slot options 5:reader* or 5:writer* instead of 5:accessor*. For example, 3(defclass thing () ((b :initform nil :reader b-value)))* defines function 3b-value* to return the value of slot 3b*, but it is not made 5setf*-able. The 5:writer* option can be used one of two ways. 3(defclass thing () ((c :writer set-c)))* defines function 3set-c* such that 3(set-c 2new-value* 2object*)* stores a value into slot 3c*. 3(defclass thing () ((c :writer (setf get-c)))*) enables doing 3(setf (get-c 2object*) 2new-value*)* to store a value. (Note, however, that 5slot-value* can always be used to either read or write the slot regardless of these options.) Any combination of these three options may be used on the same slot, including multiple uses of the same option to define more than one function name to do the same thing. Two other commonly used slot options are 5:type*, which can be used to declare that all values for the slot will be of a specified type, and 5:documentation* which specifies a documentation string describing the slot. For example: 3(defclass stack () *(3(x :initform nil :type list* 3 :documentation "The list representing the stack.")))* You can also attach a documentation string to the class as a whole, by using the 5:documentation* class option following the list of slot descriptions: 3(defclass 2name* () (2slots* 2...*) (:documentation "2string*"))* You may have noticed that most of what we have done so far could have been done just as well by using 5defstruct* instead of 5defclass*. However, this is only the beginning; CLOS provides many features beyond what can be done with structures. The last slot option is 5:allocation*, which has a default value of 5:instance*, meaning that the slot is included in each instance of the class. The option 5:allocation :class* means that the slot is allocated just once as part of the class description, so that the same slot is shared by all instances. 1Generic functions and methods* So far, we have discussed classes as specifying the slots which are contained in an instance. Classes are also characterized by the operations which can be performed on instances. These operations are defined in terms of 2generic functions* and 2methods*. A 2generic function* is a function that is called like any ordinary function, but the code that is actually executed will be one or more of a collection of 2methods*, which can be separately defined. The method to be run is chosen by matching the classes of the actual function arguments received with the classes accepted by the method. In other words, a method defines what the generic function will do for a particular class of argument. Returning to the example of a class of objects describing positions in two-dimensional space: 3(defclass point () ((x :initarg :x) (y :initarg :y)))* we might have functions to operate on these objects such as: 3(defun range (obj) * 3 (with-slots (x y) obj* 3 (sqrt (+ (expt x 2) (expt y 2)))))* so that 3(range (make-instance 'point :x 3 :y 4)) => 5.0* Suppose, though, that we had another class that describes positions using polar notation: 3(defclass polar-point () ((r :initarg :r) (angle :initarg :angle)))* The concept of range could also apply to instances of this class, but it would consist of just returning the instance variable 3r* instead of the previous calculation. Thus you might think of doing something like: 3(defun range (obj)* 3 (etypecase obj* 3 (point (with-slots (x y) obj (sqrt (+ (expt x 2) (expt y 2)))))* 3 (polar-point (slot-value obj 'r))))* This will work, but there is a better way. What we really want to do is to define methods for a generic function. For example: 3(defmethod range2 ((obj point))* 3 (with-slots (x y) obj (sqrt (+ (expt x 2) (expt y 2)))))* 3(defmethod range2 ((obj polar-point))* 3 (slot-value obj 'r))* Here we have defined two methods for generic function 3range2*, the first to be invoked if the argument is a 3point*, and the second to be invoked if the argument is a 3polar-point*. 3(range2 (make-instance 'point :x 3 :y 4)) * 3=> 5.0* 3(range2 (make-instance 'polar-point :r 7 :angle 0)) * 3=> 7* The function definition cell of the symbol 3range2* contains code which will dispatch to the appropriate method depending on the class of the argument. This automatic dispatch is actually faster than using 5typecase*, and we have the advantage that the methods can be defined and changed separately from each other. Also, slot access within a method is optimized to be more efficient since the class of the object is known. The slot accessors described earlier as being created by the slot options 5:reader*, 5:writer*, or 5:accessor* are actually defined as methods, so the same function name can be used in more than one class. Thus instead of the second 3defmethod* above, we could have gotten the same result with: 3(defclass polar-point () ((r :initarg :r :reader range2)* 3 (angle :initarg* 3:angle)))* Note that the syntax of 5defmethod* is very similar to 5defun*: 3(defmethod 2function-name* (2argument* 2...*) 2body-form* 2...*)* The only difference is in the lambda-list; while in a 5defun*, required arguments are represented by just a symbol, in a 5defmethod*, required arguments may be 2specialized* by declaring the class for which the method is applicable. This is done using a list of 3(2argument-name* 2class-name*)*. A class name of 3t* is equivalent to just the argument name alone, meaning that the argument can be anything; a method for class 3t* would be invoked as a default if no more specific method was applicable. More than one argument can be specialized, and selection doesn't have to be done on the first argument. For example in 3(defmethod hoot (a (b bird) c &optional d) 2...*)* 3(defmethod hoot (a (b fish) c &optional d) 2...*)* we have a generic function which accepts three or four arguments and dispatches on the class of the second argument. Note that optional and keyword arguments cannot be specialized. Most of the predefined type name symbols of Common Lisp are also defined as class names so that they can be used for method dispatch. For example, we could do 3(defmethod range2 ((obj number)) (abs obj))* to extend generic function 3range2* to operate on numbers: 3(range2 #c(2 5)) => 5.3851647* Note that while these 2built-in types* are considered classes for the purpose of method dispatching, they do not have slots and cannot be used as the argument to 5make-instance*. Named structures and flavor names can also be used as method specializers. In the examples so far, the first use of a 5defmethod* has implied that the symbol is to be defined as a generic function if it isn't defined already. There is also a 5defgeneric* macro which can be used to explicitly declare a generic function. It has the form: 3(defgeneric 2function-name* (2argument* 2...*) 2option* 2...*)* This provides a place to declare a documentation string or arglist for the generic function (a 5defmethod* can have a documentation string, but it only applies to that method). For example: 3(defgeneric range2 (obj) * 3 (declare (arglist point) (values range))* 3 (:documentation "Return the distance of the point from the origin."))* defines information to be displayed by 3CTRL-SHIFT-A* and 3CTRL-SHIFT-D* for 3range2*. There are a number of other options to 5defgeneric*, but most are more advanced features that won't be discussed here. 1Slots revisited* We have seen that slots can be accessed by either 5slot-value*, 5with-slots*, or by accessor functions declared as slot options, and you may be wondering which is best to use. For reasons of both efficiency and good program design (modularity and information hiding), the guideline is: * Within a method, it is best to use 5slot-value* (or 5with-slots* which is just an abbreviation for 5slot-value*) to access slots of the specialized arguments of the method. The compiler optimizes these references to be just an indirect memory reference instead of a function call, so they are only slightly less efficient than local variables. * Elsewhere, it is better to use accessor functions. (There is a macro called 5with-accessors* which provides an abbreviated notation for using accessors, similar to what 5with-slots* does for 5slot-value*.) Since a function call really is involved, it is better to make a local copy than to make repeated calls to the accessor. If it is necessary to use 5slot-value* or 5with-slots* on something other than a specialized argument, the compiler can partially optimize the reference if the type of the instance is declared. For example: 3(with-slots (x y) (the point object) 2...*) 1Class inheritance** The second argument to 5defclass* is a list of classes which are inherited by the class being defined. Instances of the new class include all of the slots defined in all of the inherited classes, and all operations defined for the inherited classes can also be used on the new class. For example, we could define a class to describe objects in three-dimensional space by extending the definition of two-dimensional points: 3(defclass 3d-point (point) ((z :initarg :z)))* 3(setq p (make-instance '3d-point :x 1 :y 2 :z 3))* 3(slot-value p 'x) * 3=> 1* 3(slot-value p 'y) * 3=> 2* 3(slot-value p 'z) * 3=> 3* 3(range2 p) * 3=> 2.236068* The new class is called a 2subclass* of each inherited class, which is a 2superclass* of the classes that use it. 3(typep p 'point) * 3=> t* 3(typep p '3d-point) * 3=> t* 3(subtypep '3d-point 'point) * 3=> t* Note that the ability to inherit from more than one class is a significant advantage of CLOS over C++. 1Method combination* When using a hierarchy of inherited classes, sometimes we want a method to be inherited, and sometimes we want it to be shadowed, and sometimes we want some combination. For example, given 3(defclass low () (a))* 3(defclass high (low) (b))* 3(defmethod tweek ((x low)) (print "tweek low"))* Then calling generic function 3tweek* with an instance of class 3high* will use the method inherited from class 3low*: 3(tweek (make-instance 'high)) => "tweek low"* We could, however, define a method for 3high* and it will then get used instead: 3(defmethod tweek ((x high)) (print "tweek high"))* 3(tweek (make-instance 'low)) => "tweek low"* 3(tweek (make-instance 'high)) => "tweek high"* When more than one method is applicable, the one for the most specific class will be invoked. Suppose now that we want the action taken for the subclass to do the action defined for the superclass and do something else besides. In other words, we want some combination of the applicable methods. There are two ways this can be done. The first is for the more specific method to use the function 5call-next-method *to invoke the next-most specific method. For example: 3(defmethod ding ((x low)) (format t "ding low"))* 3(defmethod ding ((x high))* 3 (format t "first, ")* 3 (call-next-method)* 3 (format t ", finally"))* 3(ding (make-instance 'low)) *prints 3 ding low* 3(ding (make-instance 'high)) * prints 3first, ding low, finally* When 5call-next-method* is used without any arguments, the next-most specific method is called using the same arguments that were originally received by the current method. If 5call-next-method* is used with arguments, then those argument values are passed to the next method instead. The result value of 5call-next-method* is the value returned by the method invoked. An error is signalled by 5call-next-method* if there isn't any next method to call, so the function 5next-method-p* can be called to test whether there is another method for 5call-next-method* to use: ... 3(when (next-method-p) (call-next-method))* ... The other way of combining methods is by using method qualifiers such as 5:before* and 5:after*. This approach is less general than using 5call-next-method* but may be more convienient for many of the most common uses of method combination. For example: 3(defmethod twang ((x low)) (format t "twang low"))* 3(defmethod twang :before ((x high)) (format t "before high; "))* 3(defmethod twang :after ((x high)) (format t "; after high"))* 3(twang (make-instance 'high)) *prints3 before high; twang low; after high* Here the 2method qualifier* 5:before* or 5:after* is used in the 5defmethod* to indicate that the method being defined is to be run either before or after any other methods that may apply. The value returned by the generic function is the value returned by the most-specific method which is not qualified by 5:before* or 5:after*. In other words, the value returned by a 5:before* or 5:after* method is not used. Qualified methods are also called 2auxiliary* 2methods* and non-qualified methods are called 2primary* 2methods*. A method can also be qualified as an 5:around* method, in which case 5call-next-method* can be used in the method body to invoke the primary method. This looks like an unqualified method except that all 5:around* methods are run before any of the primary methods. For example: 3(defmethod twing ((x low)) 'low) * 3(defmethod twing ((x high)) 'high)* 3(defmethod twing :around ((x low)) (list 'a (call-next-method) 'z))* 3(twing (make-instance 'high)) =>* 3(A HIGH Z)* Any combination of these may be used to create many applicable methods for a particular generic function and argument classes. The general rule for standard method combination is that all 5:around* methods are run first (most specific first), then all 5:before* methods (most specific first), then the most specific primary method (which may use 5call-next-method* to invoke other primary methods), and finally all of the 5:after* methods are run (most specific last). An error is signalled if there isn't at least one applicable primary method. There are also some other method combination types that can be specified as an option to 5defgeneric*. In the following example, all applicable methods are called and the results are combined into a list. 3(defgeneric numlis (x) (:method-combination list))* 3(defmethod numlis list ((x low)) 1)* 3(defmethod numlis list ((x high)) 2)* 3(numlis (make-instance 'high)) => (2 1)* By default, the result of the most specific method comes first, but this can be reversed by saying, for example, 3(defgeneric rnumlis (x) (:method-combination list :most-specific-last))* The method combination types that can be used like this to combine the results are: 5+*, 5and*, 5append*, 5list*, 5max*, 5min*, 5nconc*, 5or*, and 5progn*. For each of these, the function with the same name is used to combine the method results. If you want to use some other function to combine the results, you can use the macro 5define-method-combination* to enable doing that. For example, if you wanted the results to be combined by 5string-append*, you could do: 3(define-method-combination string-append)* 3(defgeneric words (x) (:method-combination string-append))* 3(defmethod words string-append ((x low)) "wagon")* 3(defmethod words string-append ((x high)) "red ")* 3(words (make-instance 'high)) => "red wagon"* 1Initialization of instances* One difference between CLOS and Flavors is what happens when the definition of a class is changed. In Flavors, old instances continue to be associated with the old definition of the flavor. CLOS, however, automatically updates any existing instances to match the current class definition when the instance is accessed the next time. For example: 3(defclass fudge () ((a :initform 1)))* 3(setq object (make-instance 'fudge))* 3(slot-exists-p object 'b) => nil* 3(defclass fudge () ((a :ini*t3form 1) (b :initform 2)))* 3(slot-value object 'b) => 2* When 5make-instance* creates a new object, it calls generic function 5initialize-instance* to initialize the slots; it in turn calls generic function 5shared-initialize* to do most of the work. Re-initialization of an instance when the class definition changes also uses 5shared-initialize*. The user can define methods for either of these functions in order to customize the initialization. The most common thing to want to do is to perform some action after the default initialization. For example: 3(defclass ddd () ((a :initarg :a) (b :initarg :b)(c :initarg :c)))* 3(defmethod shared-initialize :after ((obj ddd) islots &rest args)* 3 (declare (ignore islots args))* 3 (unless (slot-boundp obj 'c)* 3 (with-slots (a b c) obj* 3 (setf c (+ a b)))))* 3(slot-value (make-instance 'ddd :a 5 :b 10) 'c) => 15 1Individual classes** We have seen that method specializers are symbols which are the names of classes. In general, type names which are lists are not permitted as method specializers. There is, however, one exception: a list of the form 3(eql 2form*)* can be used as the class of a method argument. This means that the method applies when the argument is 5eql* to the object that results from evaluating 2form* (which is evaluated once at 5defmethod* time)2.* In other words, this specifies a type that contains only one object. For example, this could be used to dispatch on keywords: 3(defmethod key-check ((key (eql :yes))) "The answer is yes.")* 3(defmethod key-check ((key (eql :no))) "The answer is no.")* 3(key-check :yes) => "The answer is yes."* 3(key-check :no) => "The answer is no."* 1setf functions* Generic functions can also be used to define how to 5setf* something. We have seen how the expression 3(foo x)* can be defined by 3(defmethod foo (2...*)2...*)*; similarly we can define the meaning of 3(setf (foo x) v)* by 3(defmethod (setf foo) (2...*) 2...*)*. A list beginning with the symbol 5setf* is a function-spec which specifies how to 5setf* the function which is the second element of the list. The value to be stored is the first argument of the 3(setf foo)* function, with the remaining arguments being the same as for 3foo*. 1Utility functions* Here are some more assorted utility functions that are part of CLOS: 3(find-class 2symbol*)* returns the object describing the class named by 2symbol*. 3(find-class 2symbol* nil)* returns the class object, or 3nil* if not defined. 3(setf (find-class 2symbol*) nil)* removes the definition of a class. 3(class-of 2object*)* returns the class of which 2object* is an instance. (class-name 2class-object*) returns the symbol which is the name of a class object. 3(find-method 2generic-function-object* 2method-qualifiers* 2specializers** 2error-p*) looks up a method. For example: 3(find-method #'tweek nil (list (find-class 'low))) => * 3#* 3(remove-method* 2generic-function-object* 2method3)** un-defines a method, where 2method* is a method object such as returned by 5find-method*. Since 5find-method* and 5remove-method* are a little awkward to use, a simpler way to undo a method on the Explorer is: 3(fundefine '(ticlos:method 2function-spec* 2specializers*))* For example: 3(fundefine '(ticlos:method tweek (low)))* An even simpler way to undo a 5defmethod* is to edit the form, adding "un" to make it 5undefmethod*, and then evaluate it. Both 5fundefine* and 5undefmethod* are in the 3TICL* package. 1For further reading* There is a brief reference summary of all of the user-callable CLOS functions in file 3"REFERENCE.TEXT"* in this same directory. With the file in a Zmacs buffer, you can use 3CTRL-SHIFT-D* to get more information. If you are familiar with Flavors, the file 3"CLOS-VS-FLAVORS.TEXT"* may help clarify how CLOS differs from Flavors. A more complete user's guide to CLOS can be found in the book 2Object-Oriented Programming in Common Lisp* by Sonya E. Keene, published by Addison-Wesley, 1989. A good introduction to the concepts of object-oriented programming and how they are embodied in CLOS can be found in the article ``The Common Lisp Object System'' by Richard Gabriel in 2AI Expert*, March 1989, page 54 and following. The official reference manual for CLOS currently consists of chapters 1 and 2 of ANSI committee X3J13 document 88-002R (June 1988), which has been published as a special issue of 2SIGPLAN Notices*, September, 1988. (There is also a chapter 3, in document 89-003, but it is unfinished and is only partially supported by the current TI implementation.) The file 3"RELEASE-NOTES.TEXT"* in this directory describes TI extensions to CLOS and known bugs and limitations.