;;; -*- Mode:gate; Fonts:(HL12 HL12I HL12B CPTFONTB HL12BI HL12B HL12I ) -*- =Node: 4Mixing Flavors* =Text: 3MIXING FLAVORS* Now we have a system for defining message-receiving objects so that we can have generic operations. If we want to create a new type called 2meteor* that would accept the same generic operations as 2ship*, we could simply write another 2defflavor* and two more 2defmethod*'s that looked just like those of 2ship*, and then meteors and ships would both accept the same operations. 2ship* would have some more instance variables for holding attributes specific to ships and some more methods for operations that are not generic, but are only defined for ships; the same would be true of 2meteor*. However, this would be a a wasteful thing to do. The same code has to be repeated in several places, and several instance variables have to be repeated. The code now needs to be maintained in many places, which is always undesirable. The power of flavors (and the name ``flavors'') comes from the ability to mix several flavors and get a new flavor. Since the functionality of 2ship* and 2meteor* partially overlap, we can take the common functionality and move it into its own flavor, which might be called 2moving-object*. We would define 2moving-object* the same way as we defined 2ship* in the previous section. Then, 2ship* and 2meteor* could be defined like this: 3(defflavor ship (engine-power number-of-passengers name) * 3 (moving-object)* 3 :gettable-instance-variables)* 3(defflavor meteor (percent-iron)* 3 (moving-object)* 3 :inittable-instance-variables)* These 2defflavor* forms use the second subform, which we ignored previously. The second subform is a list of flavors to be combined to form the new flavor; such flavors are called 1components*. Concentrating on 2ship* for a moment (analogous things are true of 2meteor*), we see that it has exactly one component flavor: 2moving-object*. It also has a list of instance variables, which includes only the ship-specific instance variables and not the ones that it shares with 2meteor*. By incorporating 2moving-object*, the 2ship* flavor acquires all of its instance variables, and so need not name them again. It also acquires all of 2moving-object*'s methods, too. So with the new definition, 2ship* instances still implement the 2:x-velocity* and 2:speed* operations, with the same meaning as before. However, the 2:engine-power* operation is also understood (and returns the value of the 2engine-power* instance variable). What we have done here is to take an abstract type, 2moving-object*, and build two more specialized and powerful abstract types on top of it. Any ship or meteor can do anything a moving object can do, and each also has its own specific abilities. This kind of building can continue; we could define a flavor called 2ship-with-passenger* that was built on top of 2ship*, and it would inherit all of 2moving-object*'s instance variables and methods as well as 2ship*'s instance variables and methods. Furthermore, the second subform of 2defflavor* can be a list of several components, meaning that the new flavor should combine all the instance variables and methods of all the flavors in the list, as well as the ones 1those* flavors are built on, and so on. All the components taken together form a big tree of flavors. A flavor is built from its components, its components' components, and so on. We sometimes use the term ``components'' to mean the immediate components (the ones listed in the 2defflavor*), and sometimes to mean all the components (including the components of the immediate components and so on). (Actually, it is not strictly a tree, since some flavors might be components through more than one path. It is really a directed graph; it can even be cyclic.) The order in which the components are combined to form a flavor is important. The tree of flavors is turned into an ordered list by performing a 1top-down, depth-first* walk of the tree, including non-terminal nodes 1before* the subtrees they head, ignoring any flavor that has been encountered previously somewhere else in the tree. For example, if 2flavor-1*'s immediate components are 2flavor-2* and 2flavor-3*, and 2flavor-2*'s components are 2flavor-4* and 2flavor-5*, and 2flavor-3*'s component was 2flavor-4*, then the complete list of components of 2flavor-1* would be: 3flavor-1, flavor-2, flavor-4, flavor-5, flavor-3* The flavors earlier in this list are the more specific, less basic ones; in our example, 2ship-with-passengers* would be first in the list, followed by 2ship*, followed by 2moving-object*. A flavor is always the first in the list of its own components. Notice that 2flavor-4* does not appear twice in this list. Only the first occurrence of a flavor appears; duplicates are removed. (The elimination of duplicates is done during the walk; if there is a cycle in the directed graph, it does not cause a non-terminating computation.) The set of instance variables for the new flavor is the union of all the sets of instance variables in all the component flavors. If both 2flavor-2* and 2flavor-3* have instance variables named 2foo*, then 2flavor-1* has an instance variable named 2foo*, and any methods that refer to 2foo* refer to this same instance variable. Thus different components of a flavor can communicate with one another using shared instance variables. (Typically, only one component ever sets the variable; the others only look at it.) The default initial value for an instance variable comes from the first component flavor to specify one. The way the methods of the components are combined is the heart of the flavor system. When a flavor is defined, a single function, called a 1combined method*, is constructed for each operation supported by the flavor. This function is constructed out of all the methods for that operation from all the components of the flavor. There are many different ways that methods can be combined; these can be selected by the user when a flavor is defined. The user can also create new forms of combination. There are several kinds of methods, but so far, the only kinds of methods we have seen are 1primary* methods. The default way primary methods are combined is that all but the earliest one provided are ignored. In other words, the combined method is simply the primary method of the first flavor to provide a primary method. What this means is that if you are starting with a flavor 2foo* and building a flavor 2bar* on top of it, then you can override 2foo*'s method for an operation by providing your own method. Your method will be called, and 2foo*'s will never be called. Simple overriding is often useful; for example, if you want to make a new flavor 2bar* that is just like 2foo* except that it reacts completely differently to a few operations. However, often you don't want to completely override the base flavor's (2foo*'s) method; sometimes you want to add some extra things to be done. This is where combination of methods is used. The usual way methods are combined is that one flavor provides a primary method, and other flavors provide 1daemon methods*. The idea is that the primary method is ``in charge'' of the main business of handling the operation, but other flavors just want to keep informed that the message was sent, or just want to do the part of the operation associated with their own area of responsibility. 1daemon* methods come in two kinds, 1before* and 1after*. There is a special syntax in 2defmethod* for defining such methods. Here is an example of the syntax. To give the 2ship* flavor an after-daemon method for the 2:speed* operation, the following syntax would be used: 3(defmethod (ship :after :speed) () 1body*)* Now, when a message is sent, it is handled by a new function called the 1combined* method. The combined method first calls all of the before daemons, then the primary method, then all the after daemons. Each method is passed the same arguments that the combined method was given. The returned values from the combined method are the values returned by the primary method; any values returned from the daemons are ignored. Before-daemons are called in the order that flavors are combined, while after-daemons are called in the reverse order. In other words, if you build 2bar* on top of 2foo*, then 2bar*'s before-daemons run before any of those in 2foo*, and 2bar*'s after-daemons run after any of those in 2foo*. The reason for this order is to keep the modularity order correct. If we create 2flavor-1* built on 2flavor-2*, then it should not matter what 2flavor-2* is built out of. Our new before-daemons go before all methods of 2flavor-2*, and our new after-daemons go after all methods of 2flavor-2*. Note that if you have no daemons, this reduces to the form of combination described above. The most recently added component flavor is the highest level of abstraction; you build a higher-level object on top of a lower-level object by adding new components to the front. The syntax for defining daemon methods can be found in the description of 2defmethod* below. To make this a bit more clear, let's consider a simple example that is easy to play with: the 2:print-self* method. The Lisp printer (i.e. the 2print* function; see 4(READPRINT-1)What the Printer Produces*) prints instances of flavors by sending them 2:print-self* messages. The first argument to the 2:print-self* operation is a stream (we can ignore the others for now), and the receiver of the message is supposed to print its printed representation on the stream. In the 2ship* example above, the reason that instances of the 2ship* flavor printed the way they did is because the 2ship* flavor was actually built on top of a very basic flavor called 2vanilla-flavor*; this component is provided automatically by 2defflavor*. It was 2vanilla-flavor*'s 2:print-self* method that was doing the printing. Now, if we give 2ship* its own primary method for the 2:print-self* operation, then that method completely takes over the job of printing; 2vanilla-flavor*'s method will not be called at all. However, if we give 2ship* a before-daemon method for the 2:print-self* operation, then it will get invoked before the 2vanilla-flavor* method, and so whatever it prints will appear before what 2vanilla-flavor* prints. So we can use before-daemons to add prefixes to a printed representation; similarly, after-daemons can add suffixes. There are other ways to combine methods besides daemons, but this way is the most common. The more advanced ways of combining methods are explained in a later section; see 4(FLAVOR-4)Method Combination*. 2vanilla-flavor* and what it does for you are also explained later; see 4(FLAVOR-4)Vanilla Flavor*. =Node: 4Flavor Functions* =Text: 3FLAVOR FUNCTIONS defflavor* 1Macro* A flavor is defined by a form 3(defflavor 1flavor-name* (1var1* 1var2*...) (1flav1* 1flav2*...)* 3 1opt1* 1opt2*...)* 1flavor-name* is a symbol which serves to name this flavor. It is given an 2si:flavor* property which is the internal data-structure containing the details of the flavor. 2(type-of 1obj*)*, where 1obj* is an instance of the flavor named 1flavor-name*, returns the symbol 1flavor-name*. 2(typep 1obj* 1flavor-name*)* is 2t* if 1obj* is an instance of a flavor, one of whose components (possibly itself) is 1flavor-name*. 1var1*, 1var2*, etc. are the names of the instance-variables containing the local state for this flavor. A list of the name of an instance-variable and a default initialization form is also acceptable; the initialization form is evaluated when an instance of the flavor is created if no other initial value for the variable is obtained. If no initialization is specified, the variable remains void. 1flav1*, 1flav2*, etc. are the names of the component flavors out of which this flavor is built. The features of those flavors are inherited as described previously. 1opt1*, 1opt2*, etc. are options; each option may be either a keyword symbol or a list of a keyword symbol and arguments. The options to 2defflavor* are described in 4(FLAVOR-3)Defflavor Options*. 3*all-flavor-names** 1Variable* A list of the names of all the flavors that have ever been 2defflavor*'ed. 3defmethod* 1Macro* A method, that is, a function to handle a particular operation for instances of a particular flavor, is defined by a form such as 3(defmethod (1flavor-name* 1method-type* 1operation*) 1lambda-list** 3 1form1* 1form2*...)* 1flavor-name* is a symbol which is the name of the flavor which is to receive the method. 1operation* is a keyword symbol which names the operation to be handled. 1method-type* is a keyword symbol for the type of method; it is omitted when you are defining a primary method. For some method-types, additional information is expected. It comes after 1operation*. The meaning of 1method-type* depends on what style of method combination is declared for this operation. For instance, if 2:daemon* combination (the default style) is in use, method types 2:before* and 2:after* are allowed. See 4(FLAVOR-4)Method Combination* for a complete description of method types and the way methods are combined. 1lambda-list* describes the arguments and aux variables of the function; the first argument to the method, which is the operation name itself, is automatically handled and so is not included in the lambda-list. Note that methods may not have unevaluated (2"e*) arguments; that is, they must be functions, not special forms. 1form1*, 1form2*, etc. are the function body; the value of the last form is returned. The variant form 3(defmethod (1flavor-name* 1operation*) 1function*)* where 1function* is a symbol, says that 1flavor-name*'s method for 1operation* is 1function*, a symbol which names a function. That function must take appropriate arguments; the first argument is the operation. When the function is called, 2self* will be bound. If you redefine a method that is already defined, the old definition is replaced by the new one. Given a flavor, an operation name, and a method type, there can only be one function (with the exception of 2:case* methods; see 4(FLAVOR-4)Method Combination*), so if you define a 2:before* daemon method for the 2foo* flavor to handle the 2:bar* operation, then you replace the previous before-daemon; however, you do not affect the primary method or methods of any other type, operation or flavor. The function spec for a method (see 4(FUNCTIONS-1)Function Specs*) looks like: 3(:method 1flavor-name* 1operation*) 2or** 3(:method 1flavor-name* 1method-type* 1operation*) 2or** 3(:method 1flavor-name* 1method-type* 1operation* 1suboperation*)* This is useful to know if you want to trace (4(DEBUGGING-5)Tracing Function Execution*), breakon (4(DEBUGGING-5)Breakon*) or advise (4(DEBUGGING-5)Advising a Function*) a method, or if you want to poke around at the method function itself, e.g. disassemble it (see 4(MISCELL-1)Poking Around in the Lisp World*). 3make-instance* 1flavor-name* 1init-option1* 1value1* 1init-option2* 1value2...* Creates and returns an instance of the specified flavor. Arguments after the first are alternating init-option keywords and arguments to those keywords. These options are used to initialize instance variables and to select arbitrary options, as described above. An 2:init* message is sent to the newly-created object with one argument, the init-plist. This is a disembodied property-list containing the init-options specified and those defaulted from the flavor's 2:default-init-plist* (however, init keywords that simply initialize instance variables, and the corresponding values, may be absent when the 2:init* methods are called). 2make-instance* is an easy-to-call interface to 2instantiate-flavor*, below. If 2:allow-other-keys* is used as an init keyword with a non-2nil* value, this error check is suppressed. Then unrecognized keywords are simply ignored. Example: 3(make-instance 'foo :lose 5 :allow-other-keys t)* specifies the init keyword 2:lose*, but prevents an error should the keyword not be handled. 3instantiate-flavor* 1flavor-name* 1init-plist* &optional 1send-init-message-p* 1return-unhandled-keywords* 1area* This is an extended version of 2make-instance*, giving you more features. Note that it takes the 1init-plist* as an individual argument, rather than taking a rest argument of init options and values. The 1init-plist* argument must be a disembodied property list; 2locf* of a rest argument is satisfactory. Beware! This property list can be modified; the properties from the default init plist are 2putprop*'ed on if not already present, and some 2:init* methods do explicit 2putprop*'s onto the 1init-plist*. In the event that 2:init* methods 2remprop* properties already on the 1init-plist* (as opposed to simply doing 2get* and 2putprop*), then the 1init-plist* is 2rplacd*'ed. This means that the actual supplied list of options is modified. It also means that 2locf* of a rest argument does not work; the caller of 2instantiate-flavor* must copy its rest argument (e.g. with 2copylist*); this is because 2rplacd* is not allowed on stack lists. Do not use 2nil* as the 1init-plist* argument. This would mean to use the properties of the symbol 2nil* as the init options. If your goal is to have no init options, you must provide a property list containing no properties, such as the list 2(nil)*. Here is the sequence of actions by which 2instantiate-flavor* creates a new instance: First, the specified flavor's instantiation flavor function (4(FLAVOR-3)Defflavor Options*), if it exists, is called to determine which flavor should actually be instantiated. If there is no instantiation flavor function, the specified flavor is instantiated. If the flavor's method hash-table and other internal information have not been computed or are not up to date, they are computed. This may take a substantial amount of time or even invoke the compiler, but it happens only once for each time you define or redefine a particular flavor. Next, the instance itself is created. If the 1area* argument is specified, it is the number of an area in which to cons the instance; otherwise the flavor's instance area function is called to choose an area if there is one; otherwise, 2default-cons-area* is used. See 4(FLAVOR-3)Defflavor Options*. Then the initial values of the instance variables are computed. If an instance variable is declared inittable, and a keyword with the same spelling as its name appears in 1init-plist*, the property for that keyword is used as the initial value. Otherwise, if the default init plist specifies such a property, it is evaluated and the value is used. Otherwise, if the flavor definition specifies a default initialization form, it is evaluated and the value is used. The initialization form may not refer to any instance variables. It can find the new instance in 2self* but should not invoke any operations on it and should not refer directly to any instance variables. It can get at instance variables using accessor macros created by the 2:outside-accessible-instance-variables* option (4(FLAVOR-3)Defflavor Options*) or the function 2symeval-in-instance* (4(FLAVOR-2)Flavor Functions*). If an instance variable does not get initialized either of these ways it is left void; an 2:init* method may initialize it (see below). All remaining keywords and values specified in the 2:default-init-plist* option to 2defflavor*, that do not initialize instance variables and are not overridden by anything explicitly specified in 1init-plist* are then merged into 1init-plist* using 2putprop*. The default init plist of the instantiated flavor is considered first, followed by those of all the component flavors in the standard order. See 4(FLAVOR-3)Defflavor Options*. Then keywords appearing in the 1init-plist* but not defined with the 2:init-keywords* option or the 2:inittable-instance-variables* option for some component flavor are collected. If the 2:allow-other-keys* option is specified with a non-2nil* value (either in the original 1init-plist* argument or by some default init plist) then these 1unhandled* keywords are ignored. If the 1return-unhandled-keywords* argument is non-2nil*, a list of these keywords is returned as the second value of 2instantiate-flavor*. Otherwise, an error is signaled if any unrecognized init keywords are present. If the 1send-init-message-p* argument is supplied and non-2nil*, an 2:init* message is sent to the newly-created instance, with one argument, the 1init-plist*. 2get* can be used to extract options from this property-list. Each flavor that needs initialization can contribute an 2:init* method by defining a daemon. The 2:init* methods should not look on the 1init-plist* for keywords that simply initialize instance variables (that is, keywords defined with 2:inittable-instance-variables* rather than 2:init-keywords*). The corresponding instance variables are already set up when the 2:init* methods are called, and sometimes the keywords and their values may actually be missing from the 1init-plist* if it is more efficient not to put them on. To avoid problems, always refer to the instance variables themselves rather than looking for the init keywords that initialize them. 3:init* 1init-plist* 1Operation on all flavor instances* This operation is implemented on all flavor instances. Its purpose is to examine the init keywords and perform whatever initializations are appropriate. 1init-plist* is the argument that was given to 2instantiate-flavor*, and may be passed directly to 2get* to examine the value of any particular init option. The default definition of this operation does nothing. However, many flavors add 2:before* and 2:after* daemons to it. 3instancep* 1object* Returns 2t* if 2object* is an instance. This is equivalent to 2(typep 1object* 'instance)*. 3defwrapper* 1Macro* This is hairy and if you don't understand it you should skip it. Sometimes the way the flavor system combines the methods of different flavors (the daemon system) is not powerful enough. In that case 2defwrapper* can be used to define a macro that expands into code that is wrapped around the invocation of the methods. This is best explained by an example; suppose you needed a lock locked during the processing of the 2:foo* operation on flavor 2bar*, which takes two arguments, and you have a 2lock-frobboz* special-form that knows how to lock the lock (presumably it generates an 2unwind-protect*). 2lock-frobboz* needs to see the first argument to the operation; perhaps that tells it what sort of operation is going to be performed (read or write). 3(defwrapper (bar :foo) ((arg1 arg2) . body)* 3 `(lock-frobboz (self arg1)* 3 . ,body))* The use of the 2body* macro-argument prevents the macro defined by 2defwrapper* from knowing the exact implementation and allows several 2defwrapper*'s from different flavors to be combined properly. Note well that the argument variables, 2arg1* and 2arg2*, are not referenced with commas before them. These may look like 2defmacro* ``argument'' variables, but they are not. Those variables are not bound at the time the 2defwrapper*-defined macro is expanded and the back-quoting is done; rather the result of that macro-expansion and back-quoting is code which, when a message is sent, will bind those variables to the arguments in the message as local variables of the combined method. Consider another example. Suppose you thought you wanted a 2:before* daemon, but found that if the argument was 2nil* you needed to return from processing the message immediately, without executing the primary method. You could write a wrapper such as 3(defwrapper (bar :foo) ((arg1) . body)* 3 `(cond ((null arg1))* 3 (t (print "About to do :FOO")* 3 . ,body)))* Suppose you need a variable for communication among the daemons for a particular operation; perhaps the 2:after* daemons need to know what the primary method did, and it is something that cannot be easily deduced from just the arguments. You might use an instance variable for this, or you might create a special variable which is bound during the processing of the operation and used free by the methods. 3(defvar *communication*)* 3(defwrapper (bar :foo) (ignore . body)* 3 `(let ((*communication* nil))* 3 . ,body))* Similarly you might want a wrapper that puts a 2catch* around the processing of an operation so that any one of the methods could throw out in the event of an unexpected condition. Like daemon methods, wrappers work in outside-in order; when you add a 2defwrapper* to a flavor built on other flavors, the new wrapper is placed outside any wrappers of the component flavors. However, 1all* wrappers happen before 1any* daemons happen. When the combined method is built, the calls to the before-daemon methods, primary methods, and after-daemon methods are all placed together, and then the wrappers are wrapped around them. Thus, if a component flavor defines a wrapper, methods added by new flavors execute within that wrapper's context. 2:around* methods can do some of the same things that wrappers can. See 4(FLAVOR-4)Method Combination*. If one flavor defines both a wrapper and an 2:around* method for the same operation, the 2:around* method is executed inside the wrapper. By careful about inserting the body into an internal lambda-expression within the wrapper's code. Doing so interacts with the internals of the flavor system and requires knowledge of things not documented in the manual in order to work properly. It is much simpler to use an 2:around* method instead. 3undefmethod* 1(flavor* 1[type]* 1operation* 1[suboperation])* 1Macro* -remove-this-mt 3(undefmethod (flavor :before :operation))* removes the method created by 3(defmethod (flavor :before :operation) (1args*) ...)* To remove a wrapper, use 2undefmethod* with 2:wrapper* as the method type. 2undefmethod* is simply an interface to 2fundefine* (see 4(FUNCTIONS-2)How Programs Manipulate Function Specs*) that accepts the same syntax as 2defmethod*. If a file that used to contain a method definition is reloaded and if that method no longer seems to have a definition in the file, the user is asked whether to 2undefmethod* that method. This may be important to enable the modified program to inherit the methods it is supposed to inherit. If the method in question has been redefined by some other file, this is not done, the assumption being that the definition was merely moved. 3undefflavor* 1flavor* Undefines flavor 1flavor*. All methods of the flavor are lost. 1flavor* and all flavors that depend on it are no longer valid to instantiate. If instances of the discarded definition exist, they continue to use that definition. 3self* 1Variable* When a message is sent to an object, the variable 2self* is automatically bound to that object, for the benefit of methods which want to manipulate the object itself (as opposed to its instance variables). 3funcall-self* 1operation* 1arguments...* 3lexpr-funcall-self* 1operation* 1arguments...* 1list-of-arguments* 2funcall-self* is nearly equivalent to 2funcall* with 2self* as the first argument. 2funcall-self* used to be faster, but now 2funcall* of 2self* is just as fast. Therefore, 2funcall-self* is obsolete. It should be replaced with 2funcall* or 2send* of 2self*. Likewise, 2lexpr-funcall-self* should be replaced with use of 2lexpr-send* to 2self*. 3funcall-with-mapping-table* 1function* 1mapping-table* &rest 1arguments* Applies 1function* to 1arguments* with 2sys:self-mapping-table* bound to 1mapping-table*. This is faster than binding the variable yourself and doing an ordinary 2funcall*, because the system assumes that the mapping table you specify is the correct one for 1function* to be run with. However, if you pass the wrong mapping table, incorrect execution will take place. This function is used in the code for combined methods and is also useful for the user in 2:around* methods (see 4(FLAVOR-4)Method Combination*). 3lexpr-funcall-with-mapping-table* 1function* 1mapping-table* &rest 1arguments* Applies 1function* to 1arguments* using 2lexpr-funcall*, with 2sys:self-mapping-table* bound to 1mapping-table*. 3declare-flavor-instance-variables* 1(flavor)* 1body...* 1Macro* Sometimes it is useful to have a function which is not itself a method, but which is to be called by methods and wants to be able to access the instance variables of the object 2self*. The form 3(declare-flavor-instance-variables (1flavor-name*)* 3 (defun 1function* 1args* 1body*...))* surrounds the function definition with a peculiar kind of declaration which makes the instance variables of flavor 1flavor-name* accessible by name. Any kind of function definition is allowed; it does not have to use 2defun* per se. If you call such a function when 2self*'s value is an instance whose flavor does not include 1flavor-name* as a component, it is an error. Cleaner than using 2declare-flavor-instance-variables*, because it does not involve putting anything around the function definition, is using a local declaration. Put 2(declare (:self-flavor* 1flavorname2))** as the first expression in the body of the function. For example: 3(defun foo (a b)* 3 (declare (:self-flavor myobject))* 3 (+ a (* b speed)))* (where 2speed* is an instance variable of the flavor 2myobject*) is equivalent to 3(declare-flavor-instance-variables (myobject)* 3(defun foo (a b)* 3 (+ a (* b speed)))) with-self-variables-bound* 1body...* 1Special Form* Within the body of this special form, all of 2self*'s instance variables are bound as specials to the values inside 2self*. (Normally this is true only of those instance variables that are specified in 2:special-instance-variables* when 2self*'s flavor was defined.) As a result, inside the body you can use 2set*, 2boundp* and 2symeval*, etc., freely on the instance variables of 2self*. 3recompile-flavor* 1flavor-name* &optional 1single-operation* 1(use-old-combined-methods* 3t1)** 1(do-dependents* 3t1)** Updates the internal data of the flavor and any flavors that depend on it. If 1single-operation* is supplied non-2nil*, only the methods for that operation are changed. The system does this when you define a new method that did not previously exist. If 1use-old-combined-methods* is 2t*, then the existing combined method functions are used if possible. New ones are generated only if the set of methods to be called has changed. This is the default. If 1use-old-combined-methods* is 2nil*, automatically-generated functions to call multiple methods or to contain code generated by wrappers are regenerated unconditionally. If 1do-dependents* is 2nil*, only the specific flavor you specified is recompiled. Normally all flavors that depend on it are also recompiled. 2recompile-flavor* affects only flavors that have already been compiled. Typically this means it affects flavors that have been instantiated, but does not bother with mixins (see 4(FLAVOR-4)Flavor Families*). 3si:*dont-recompile-flavors** 1Variable* If this variable is non-2nil*, automatic recompilation of combined methods is turned off. If you wish to make several changes each of which will cause recompilation of the same combined methods, you can use this variable to speed things up by making the recompilations happen only once. Set the variable to 2t*, make your changes, and then set the variable back to 2nil*. Then use 2recompile-flavor* to recompile whichever combined methods need it. For example: 3(setq si:*dont-recompile-flavors* t)* 3(undefmethod (tv:sheet :after :bar))* 3(defmethod (tv:sheet :before :bar) ...)* 3(setq si:*dont-recompile-flavors* nil)* 3(recompile-flavor 'tv:sheet :bar)* 2tv:sheet* has very many dependents; 2recompile-flavor* even once takes painfully long. It's nice to avoid spending the time twice. 3compile-flavor-methods* 1flavor...* 1Macro* The form 2(compile-flavor-methods 1flavor-name-1* 1flavor-name-2*...)*, placed in a file to be compiled, directs the compiler to include the automatically-generated combined methods for the named flavors in the resulting QFASL file, provided all of the necessary flavor definitions have been made. Furthermore, all internal data structures needed to instantiate the flavor will be computed when the QFASL file is loaded rather than waiting until the first attempt to instantiate it. This means that the combined methods get compiled at compile time and the data structures get generated at load time, rather than both things happening at run time. This is a very good thing, since if the the compiler must be invoked at run time, the program will be slow the first time it is run. (The compiler must be called in any case if incompatible changes have been made, such as addition or deletion of methods that must be called by a combined method.) You should only use 2compile-flavor-methods* for flavors that are going to be instantiated. For a flavor that is never to be instantiated (that is, a flavor that only serves to be a component of other flavors that actually do get instantiated), it is a complete waste of time, except in the unusual case where those other flavors can all inherit the combined methods of this flavor instead of each one having its own copy of a combined method which happens to be identical to the others. In this unusual case, you should use the 2:abstract-flavor* option in 2defflavor* (4(FLAVOR-3)Defflavor Options*). The 2compile-flavor-methods* forms should be compiled after all of the information needed to create the combined methods is available. You should put these forms after all of the definitions of all relevant flavors, wrappers, and methods of all components of the flavors mentioned. The methods used by 2compile-flavor-methods* to form the combined methods that go in the QFASL file are all those present in the file being compiled and all those defined in the Lisp world. When a 2compile-flavor-methods* form is seen by the interpreter, the combined methods are compiled and the internal data structures are generated. 3get-handler-for* 1object* 1operation* Given an object and an operation, this returns the object's method for that operation, or 2nil* if it has none. When 1object* is an instance of a flavor, this function can be useful to find which of that flavor's components supplies the method. If you get back a combined method, you can use the 2Meta-X List Combined Methods* editor command (4(FLAVOR-5)Useful Editor Commands*) to find out what it does. This is related to the 2:handler* function spec (see 4(FUNCTIONS-1)Function Specs*). It is preferable to use the generic operation 2:get-handler-for*. 3flavor-allows-init-keyword-p* 1flavor-name* 1keyword* Returns non-2nil* if the flavor named 1flavor-name* allows 1keyword* in the init options when it is instantiated, or 2nil* if it does not. The non-2nil* value is the name of the component flavor that contributes the support of that keyword. 3si:flavor-all-allowed-init-keywords* 1flavor-name* Returns a list of all the init keywords that may be used in instantiating 1flavor-name*. 3symeval-in-instance* 1instance* 1symbol* &optional 1no-error-p* Returns the value of the instance variable 1symbol* inside 1instance*. If there is no such instance variable, an error is signaled, unless 1no-error-p* is non-2nil* in which case 2nil* is returned. 3set-in-instance* 1instance* 1symbol* 1value* Sets the value of the instance variable 1symbol* inside 1instance* to 1value*. If there is no such instance variable, an error is signaled. 3locate-in-instance* 1instance* 1symbol* Returns a locative pointer to the cell inside 1instance* which holds the value of the instance variable named 1symbol*. 3describe-flavor* 1flavor-name* Prints descriptive information about a flavor; it is self-explanatory. An important thing it tells you that can be hard to figure out yourself is the combined list of component flavors; this list is what is printed after the phrase `and directly or indirectly depends on'. 3si:*flavor-compilations** 1Variable* Contains a history of when the flavor mechanism invoked the compiler. It is a list; elements toward the front of the list represent more recent compilations. Elements are typically of the form 3(1function-spec* 1pathname*)* where the function spec starts with 2:method* and has a method type of 2:combined*. You may 2setq* this variable to 2nil* at any time; for instance before loading some files that you suspect may have missing or obsolete 2compile-flavor-methods* in them. 3sys:unclaimed-message* (3error*) 1Condition* This condition is signaled whenever a flavor instance is sent a message whose operation it does not handle. The condition instance supports these operations: 2:object* The flavor instance that received the message. 2:operation* The operation that was not handled. 2:arguments* The list of arguments to that operation