4 -*- Mode:Text; Package:User; Fonts:(TR12 HL12B TR12I CPTFONT HL10 CPTFONTB) -*-* 3------------------------------------------------------------------------------* 1CLOS versus Flavors* 410/18/88* 4Revised 3/18/89* 4 Copyright (C) 1988, Texas Instruments Incorporated. All rights reserved. 1Introduction** This document is intended for people who have had experience with Flavors and are learning to use CLOS; it describes the correspondence between the features of Flavors and CLOS and points out where there are differences. It needs to be kept in mind that while Flavors and CLOS provide similar capabilities, there are many significant differences in their designs, so familiarity with Flavors can lead one to assume things that aren't necessarily true in CLOS. Some considerations for Explorer programs that mix use of Flavors and CLOS will also be mentioned here. Note that we are talking here about Flavors as it exists on the Explorer, which is significantly different from the ``New Flavors'' of Symbolics Genera 7. 1Basic terminology* The following table shows the correspondence between terms for similar concepts: 3Flavors* 3 CLOS* 3-------* 3 ------* 3flavor* 3 class* 3component* 3 superclass* 3instance* 3 instance* 3object* 3 object* 3instance variable* 3 slot* 3message* 3 generic function* 3method* 3 method* 3primary method* 3 primary method* 3daemon method* 3 auxiliary method 1Flavors and classes** 5defflavor* is replaced by 5defclass*, which has similar syntax except that the second and third arguments are interchanged. Nearly all of the options have different names, however, and in 5defclass* some of the options are attached to the slot descriptions. The slot option 5:initform* is used to indicate a default initial value. Here are some 5defflavor* options and their CLOS equivalents: 5:gettable-instance-variables* -- see slot option 5:reader* 5:settable-instance-variables* -- see slot option 5:writer* (Both of these together correspond to the CLOS slot option 5:accessor*) 5:inittable-instance-variables* -- see slot option 5:initarg* 5:default-init-plist* -- see class option 5:default-initargs* 5:default-handler* -- define a method for the generic function 5no-applicable-method* 5:outside-accessible-instance-variables* -- see slot option 5:accessor* 5:accessor-prefix* -- not needed because the 5:accessor* option requires the function name to be specified explicitly. 5:alias-flavor* -- 3(setf (find-class '2new-class*) (find-class '2old-class*))* 5:method-combination* -- this is an option to 5defgeneric* instead of 5defclass*; the choice of 5:base-flavor-first* or 5:base-flavor-last* corresponds to specifying either 5:most-specific-last* or 5:most-specific-first* as the second argument of the 5:method-combination* option. 5:instantiation-flavor-function* -- this can be simulated by something like: 3(defmethod make-instance ((class (eql (find-class '2fake-class*)))* 3&rest initargs)* 3 (apply #'make-instance '2real-class* initargs))* 5:documentation* -- is also a CLOS class option. For example, the flavor definition 3(defflavor this * 3 ((alpha 'yes) beta)* 3 (other)* 3 :inittable-instance-variables* 3 (:settable-instance-variables beta)* 3 (:gettable-instance-variables beta)* 3(:method-combination (:nconc :base-flavor-first :mlist))* 3 (:documentation "Simple example"))* corresponds to the following CLOS class definition: 3(defclass this * 3 (other)* 3 ((alpha :initform 'yes :initarg :alpha)* 3 (beta :initarg :beta :accessor this-beta))* 3 (:documentation "Simple example"))* 3(defgeneric mlist (2...*) (:method-combination nconc :most-specific-last))* Note that I say ``corresponds to'', not ``is equivalent to'', because these are two different things that are similar, not two ways of doing the same thing. 3(undefflavor 2flavor-name*)* corresponds to 3(setf (find-class 2class-name*) nil)*. Flavor names and class names are both defined as type names for use by 5typep*, 5subtypep*, 5type-of*, 5the*, and 5type* declarations. The type 5instance* includes all instances of both flavors and CLOS classes. While most flavor instances are included in the type 5si:vanilla-flavor*, the type 5standard-object* includes all CLOS class instances that use the default meta-class. Classes 5standard-object* and 5si:vanilla-flavor* may be used as CLOS method specializers, but 5instance* is not currently defined as a class name. 1Inheritance* Both Flavors and CLOS support multiple inheritance. However, when combining methods there can be subtle differences in the choice of which methods are run in which order when dealing with complicated situations. If it's not obvious what's going to happen, then don't assume that the fine print of the CLOS spec matches the fine print of Flavors. 1Methods* The most obvious difference between Flavors and CLOS is that Flavors uses a concept of sending a 2message*, while CLOS uses 2generic functions*. For example, in Flavors, one would say 3(send object :do-something argument)* but the corresponding operation using CLOS would be: 3(do-something object argument)* Here, 3do-something* is a generic function which dispatches on the class of its first argument to select the method to be invoked. Both use the macro 5defmethod*, but with slightly different syntax, shown here in simplified versions: Flavors:3 (defmethod (2flavor-name* *[2daemon-type*]3 2operation*) 2lambda-list* . 2body*)* CLOS: 3 (defmethod * 2function-spec3 ** [2daemon-type*]3 * 2lambda-list3 . *body3)** where 2function-spec* is the name of the generic function. The alternate flavors syntax 3(defmethod (2flavor-name operation*)2 function*)* would need to be replaced by use of the CLOS function 5add-method*. Another important difference is that while Flavors binds the special variable 5self* to the current object when a method is invoked, a CLOS method receives the object as an argument. This allows receiving more than one object and it also means that the object is in a lexical variable that can be closed over by a lexical closure, while a dynamic closure is needed to close over 5self*. This also implies that 5defun-method* has no counterpart in CLOS. Instead of the special variable 5sys:self-mapping-table*, CLOS mapping tables are held in local variable slots that are managed automatically by the compiler and microcode. Within a flavors method, all instance variables are automatically visible as variables, but since they are tied to the dynamic value of 5self*, they do not behave as lexical variables. In a CLOS method, the macro 5with-slots* must be used to enable referencing the slots as variables; these variables are properly lexically scoped, so they behave as expected in lexical closures. Also, 5with-slots* can be used anywhere, not just in a method, although it is more efficient when used to access a specialized method argument. The macro 5compile-flavor-methods* does not have a counterpart in CLOS. As a TI extension to CLOS, the macro 5undefmethod* has been extended to work on CLOS methods as well as flavor methods. 1Instances* Flavors and CLOS both use the function 5make-instance*. The first argument can be a flavor name, class name, or class object. Whereas Flavors sends an 5:init* message to the newly created object, CLOS uses the generic functions 5initialize-instance* and 5shared-initialize*. Thus a Flavors method 3(defmethod (foo :after :init) 2...*)* corresponds to CLOS 3(defmethod initialize-instance :after ((foo obj) 2...*)* 2...3)** or 3(defmethod shared-initialize :after ((foo obj) 2...*) 2...*)* Flavors also allows passing an existing instance as the first argument to 5make-instance*, which means to re-initialize it. In CLOS, this would be done by calling the function 5reinitialize-instance*. The Flavors function 5symeval-in-instance* corresponds to the CLOS function 5slot-value*. Functions 5set-in-instance* and 5locate-in-instance* correspond to using 5setf* and 5locf* on 5slot-value*. (Incidentally, version 11 or later of the TI-CLOS system permits using 5slot-value* on flavor instances. With version 15 or later, this is equally efficient with accessing slots of CLOS instances.) 1Vanilla operations* The vanilla-flavor operations 5:print-self* and 5:describe* correspond to the CLOS generic functions 5print-object* and 5describe*. Because of the different approaches used, the operations 5:which-operations*, 5:operation-handled-p*, and 5:send-if-handles* aren't meaningful for CLOS. There isn't really a need to test whether a generic function will accept a particular set of arguments because you can just define a default method (class 3t* for all required arguments) to do what you want when a more appropriate method isn't found. 1CLOS advantages* Here is a summary of some of the features that CLOS provides that go beyond what can be done with flavors: * Method dispatching can be controlled by the class of more than one argument. * Methods can dispatch on built-in types, not just on classes. For example, you can define a generic function that has methods to handle symbol or numbers as arguments, but you can't send a flavor message to a symbol or number. * CLOS supports class slots, while Flavors only has instance slots. * Instances are automatically updated to reflect changes to the definition of their classes, whereas flavor instances are forever tied to the way the flavor was defined at the time the instance was created. Even the class of a CLOS instance can be changed at run-time. * The meta-class protocol gives users extensive abilities to customize the object system behavior. Nearly everything can be modified by user-defined methods. * CLOS permits use of anonymous classes (by using a class object wherever a class name would normally be used), while flavors always have to be globally defined. * Slots accessed via 5with-slots* behave just like lexical variables, so they can be closed over. * Unlike messages, it is easy to tell what the arguments to a generic function are and who calls it.