User Tools


Differences

This shows you the differences between two versions of the page.

Link to this comparison view

cl:macros:define-method-combination [2019/07/14 17:00]
cl:macros:define-method-combination [2019/07/17 21:00] (current)
Line 1: Line 1:
 +====== Macro DEFINE-METHOD-COMBINATION ======
 +
 +====Syntax====
 +  * **define-method-combination** //name// ⟦//​short-form-option//​⟧ → //name//
 +  * **define-method-combination** //name// //​lambda-list//​ ({//​method-group-specifier//​}''​*''​) [(**'':​arguments''​** . //​args-lambda-list//​)] [(**'':​generic-function''​** //​generic-function-symbol//​)] ⟦{//​declaration//​}''​*''​ | //​documentation//​⟧ {//​form//​}''​*''​ → //name//
 +
 +<​blockquote>​
 +short-form-option ::= **'':​documentation''​** //​documentation//​ | 
 +                      **'':​identity-with-one-argument''​** //​identity-with-one-argument//​ |
 +                      **'':​operator''​** //​operator//​
 +method-group-specifier ::= (name {{//​qualifier-pattern//​}''​+''​ | //​predicate//​} ⟦long-form-option⟧)
 +long-form-option ::= **'':​description''​** //​description//​ |
 +                     ​**'':​order''​** //order// |
 +                     ​**'':​required''​** //​required-p//​
 +</​blockquote>​
 +
 +====Arguments and Values====
 +  * //​args-lambda-list//​ - a //​[[CL:​Glossary:​define-method-combination arguments lambda list]]//.
 +  * //​declaration//​ - a **[[CL:​Symbols:​declare]]** //​[[CL:​Glossary:​expression]]//;​ not evaluated.
 +  * //​description//​ - a //​[[CL:​Glossary:​format control]]//​.
 +  * //​documentation//​ - a //​[[CL:​Glossary:​string]]//;​ not evaluated.
 +  * //forms// - an //​[[CL:​Glossary:​implicit progn]]// that must compute and return the //​[[CL:​Glossary:​form]]//​ that specifies how the //​[[CL:​Glossary:​method|methods]]//​ are combined, that is, the //​[[CL:​Glossary:​effective method]]//.
 +  * //​generic-function-symbol//​ - a //​[[CL:​Glossary:​symbol]]//​.
 +  * //​identity-with-one-argument//​ - a //​[[CL:​Glossary:​generalized boolean]]//​.
 +  * //​lambda-list//​ - //​[[CL:​Glossary:​ordinary lambda list]]//.
 +  * //name// - a //​[[CL:​Glossary:​symbol]]//​. Non-//​[[CL:​Glossary:​keyword]]//,​ //​[[CL:​Glossary:​non-nil]]//​ //​[[CL:​Glossary:​symbols]]//​ are usually used.
 +  * //​operator//​ - an //​[[CL:​Glossary:​operator]]//​. //Name// and //​operator//​ are often the //​[[CL:​Glossary:​same]]//​ //​[[CL:​Glossary:​symbol]]//​. This is the default, but it is not required.
 +  * //order// - **'':​most-specific-first''​** or **'':​most-specific-last''​**;​ evaluated.
 +  * //​predicate//​ - a //​[[CL:​Glossary:​symbol]]//​ that names a //​[[CL:​Glossary:​function]]//​ of one argument that returns a //​[[CL:​Glossary:​generalized boolean]]//​.
 +  * //​qualifier-pattern//​ - a //​[[CL:​Glossary:​list]]//,​ or the //​[[CL:​Glossary:​symbol]]//​ **[[CL:​Types:​wildcard|*]]**.
 +  * //​required-p//​ - a //​[[CL:​Glossary:​generalized boolean]]//​.
 +
 +====Description====
 +The macro **define-method-combination** is used to define new types of //​[[CL:​Glossary:​method combination]]//​.
 +
 +There are two forms of **define-method-combination**. The short form is a simple facility for the cases that are expected to be most commonly needed. The long form is more powerful but more verbose. It resembles **[[CL:​Macros:​defmacro]]** in that the body is an expression, usually using backquote, that computes a //​[[CL:​Glossary:​form]]//​. Thus arbitrary control structures can be implemented. The long form also allows arbitrary processing of method //​[[CL:​Glossary:​qualifier|qualifiers]]//​.
 +
 +===Short Form===
 +The short form syntax of **define-method-combination** is recognized when the second //​[[CL:​Glossary:​subform]]//​ is a //​[[CL:​Glossary:​non-nil]]//​ symbol or is not present. When the short form is used, //name// is defined as a type of //​[[CL:​Glossary:​method combination]]//​ that produces a Lisp form ''​(//​operator//​ //​method-call//​ //​method-call//​ ...)''​. The //​operator//​ is a //​[[CL:​Glossary:​symbol]]//​ that can be the //​[[CL:​Glossary:​name]]//​ of a //​[[CL:​Glossary:​function]]//,​ //​[[CL:​Glossary:​macro]]//,​ or //​[[CL:​Glossary:​special operator]]//​. The //​operator//​ can be supplied by a keyword option; it defaults to //name//.
 +
 +Keyword options for the short form are the following:
 +
 +  * The **'':​documentation''​** option is used to document the method-combination type; see description of long form below.
 +  * The **'':​identity-with-one-argument''​** option enables an optimization when its value is //​[[CL:​Glossary:​true]]//​ (the default is //​[[CL:​Glossary:​false]]//​). If there is exactly one applicable method and it is a primary method, that method serves as the effective method and //​operator//​ is not called. This optimization avoids the need to create a new effective method and avoids the overhead of a //​[[CL:​Glossary:​function]]//​ call. This option is designed to be used with operators such as **[[CL:​Special Operators:​progn]]**,​ **[[CL:​Macros:​and]]**,​ **[[CL:​Functions:''​+''​]]**,​ and **[[CL:​Functions:​max]]**.
 +  * The **'':​operator''​** option specifies the //​[[CL:​Glossary:​name]]//​ of the operator. The //​operator//​ argument is a //​[[CL:​Glossary:​symbol]]//​ that can be the //​[[CL:​Glossary:​name]]//​ of a //​[[CL:​Glossary:​function]]//,​ //​[[CL:​Glossary:​macro]]//,​ or //​[[CL:​Glossary:​special form]]//.
 +
 +-----
 +
 +These types of //​[[CL:​Glossary:​method combination]]//​ require exactly one //​[[CL:​Glossary:​qualifier]]//​ per method. An error is signaled if there are applicable methods with no //​[[CL:​Glossary:​qualifier|qualifiers]]//​ or with //​[[CL:​Glossary:​qualifier|qualifiers]]//​ that are not supported by the //​[[CL:​Glossary:​method combination]]//​ type.
 +
 +A //​[[CL:​Glossary:​method combination]]//​ procedure defined in this way recognizes two roles for methods. A method whose one //​[[CL:​Glossary:​qualifier]]//​ is the symbol naming this type of //​[[CL:​Glossary:​method combination]]//​ is defined to be a primary method. At least one primary method must be applicable or an error is signaled. A method with **'':​around''​** as its one //​[[CL:​Glossary:​qualifier]]//​ is an auxiliary method that behaves the same as an //​[[CL:​Glossary:​around method]]// in standard //​[[CL:​Glossary:​method combination]]//​. The //​[[CL:​Glossary:​function]]//​ **[[CL:​Functions:​call-next-method]]** can only be used in //​[[CL:​Glossary:​around method|around methods]]//;​ it cannot be used in primary methods defined by the short form of the **define-method-combination** macro.
 +
 +A //​[[CL:​Glossary:​method combination]]//​ procedure defined in this way accepts an optional argument named //order//, which defaults to **'':​most-specific-first''​**. A value of **'':​most-specific-last''​** reverses the order of the primary methods without affecting the order of the auxiliary methods.
 +
 +The short form automatically includes error checking and support for //​[[CL:​Glossary:​around method|around methods]]//​.
 +
 +For a discussion of built-in //​[[CL:​Glossary:​method combination]]//​ types, see section {\secref\BuiltInMethCombTypes}.
 +
 +===Long Form===
 +The long form syntax of **define-method-combination** is recognized when the second //​[[CL:​Glossary:​subform]]//​ is a list.
 +
 +The //​lambda-list//​ receives any arguments provided after the //​[[CL:​Glossary:​name]]//​ of the //​[[CL:​Glossary:​method combination]]//​ type in the **'':​method-combination''​** option to **[[CL:​Macros:​defgeneric]]**.
 +
 +A list of method group specifiers follows. Each specifier selects a subset of the applicable methods to play a particular role, either by matching their //​[[CL:​Glossary:​qualifier|qualifiers]]//​ against some patterns or by testing their //​[[CL:​Glossary:​qualifier|qualifiers]]//​ with a //​predicate//​. These method group specifiers define all method //​[[CL:​Glossary:​qualifier|qualifiers]]//​ that can be used with this type of //​[[CL:​Glossary:​method combination]]//​.
 +
 +The //​[[CL:​Glossary:​car]]//​ of each //​method-group-specifier//​ is a //​[[CL:​Glossary:​symbol]]//​ which //​[[CL:​Glossary:​names]]//​ a //​[[CL:​Glossary:​variable]]//​. During the execution of the //​[[CL:​Glossary:​form|forms]]//​ in the body of **define-method-combination**,​ this //​[[CL:​Glossary:​variable]]//​ is bound to a list of the //​[[CL:​Glossary:​method|methods]]//​ in the method group. The //​[[CL:​Glossary:​method|methods]]//​ in this list occur in the order specified by the **'':​order''​** option.
 +
 +If //​qualifier-pattern//​ is a //​[[CL:​Glossary:​symbol]]//​ it must be **[[CL:​Types:​wildcard|*]]**. A method matches a //​qualifier-pattern//​ if the method'​s list of //​[[CL:​Glossary:​qualifier|qualifiers]]//​ is **[[CL:​Functions:​equal]]** to the //​qualifier-pattern//​ (except that the symbol **[[CL:​Types:​wildcard|*]]** in a //​qualifier-pattern//​ matches anything). Thus a //​qualifier-pattern//​ can be one of the following: the //​[[CL:​Glossary:​empty list]]//, which matches //​[[CL:​Glossary:​unqualified methods]]//;​ the symbol **[[CL:​Types:​wildcard|*]]**,​ which matches all methods; a true list, which matches methods with the same number of //​[[CL:​Glossary:​qualifier|qualifiers]]//​ as the length of the list when each //​[[CL:​Glossary:​qualifier]]//​ matches the corresponding list element; or a dotted list that ends in the symbol **[[CL:​Types:​wildcard|*]]** (the **[[CL:​Types:​wildcard|*]]** matches any number of additional //​[[CL:​Glossary:​qualifier|qualifiers]]//​).
 +
 +Each applicable method is tested against the //​qualifier-patterns//​ and //​predicates//​ in left-to-right order. As soon as a //​qualifier-pattern//​ matches or a //​predicate//​ returns true, the method becomes a member of the corresponding method group and no further tests are made. Thus if a method could be a member of more than one method group, it joins only the first such group. If a method group has more than one //​qualifier-pattern//,​ a method need only satisfy one of the //​qualifier-patterns//​ to be a member of the group.
 +
 +The //​[[CL:​Glossary:​name]]//​ of a //​predicate//​ function can appear instead of //​qualifier-patterns//​ in a method group specifier. The //​predicate//​ is called for each method that has not been assigned to an earlier method group; it is called with one argument, the method'​s //​[[CL:​Glossary:​qualifier]]//​ //​[[CL:​Glossary:​list]]//​. The //​predicate//​ should return true if the method is to be a member of the method group. A //​predicate//​ can be distinguished from a //​qualifier-pattern//​ because it is a //​[[CL:​Glossary:​symbol]]//​ other than **[[CL:​Constant Variables:​nil]]** or **[[CL:​Types:​wildcard|*]]**.
 +
 +If there is an applicable method that does not fall into any method group, the function **[[CL:​Functions:​invalid-method-error]]** is called.
 +
 +Method group specifiers can have keyword options following the //​[[CL:​Glossary:​qualifier]]//​ patterns or predicate. Keyword options can be distinguished from additional //​[[CL:​Glossary:​qualifier]]//​ patterns because they are neither lists nor the symbol **[[CL:​Types:​wildcard|*]]**. The keyword options are as follows:
 +
 +  * The **'':​description''​** option is used to provide a description of the role of methods in the method group. Programming environment tools use ''​([[CL:​Functions:​apply]] #'​[[CL:​Functions:​format]] stream //​format-control//​ ([[CL:​Functions:​method-qualifiers]] //​method//​))''​ to print this description,​ which is expected to be concise. This keyword option allows the description of a method //​[[CL:​Glossary:​qualifier]]//​ to be defined in the same module that defines the meaning of the method //​[[CL:​Glossary:​qualifier]]//​. In most cases, //​format-control//​ will not contain any **[[CL:​Functions:​format]]** directives, but they are available for generality. If **'':​description''​** is not supplied, a default description is generated based on the variable name and the //​[[CL:​Glossary:​qualifier]]//​ patterns and on whether this method group includes the //​[[CL:​Glossary:​unqualified methods]]//​.
 +  * The **'':​order''​** option specifies the order of methods. The //order// argument is a //​[[CL:​Glossary:​form]]//​ that evaluates to **'':​most-specific-first''​** or **'':​most-specific-last''​**. If it evaluates to any other value, an error is signaled. If **'':​order''​** is not supplied, it defaults to **'':​most-specific-first''​**.
 +  * The **'':​required''​** option specifies whether at least one method in this method group is required. If its value is //​[[CL:​Glossary:​true]]//​ and the method group is empty (that is, no applicable methods match the //​[[CL:​Glossary:​qualifier]]//​ patterns or satisfy the predicate), an error is signaled. If **'':​required''​** is not supplied, it defaults to **[[CL:​Constant Variables:​nil]]**.
 +
 +------
 +
 +The use of method group specifiers provides a convenient syntax to select methods, to divide them among the possible roles, and to perform the necessary error checking. It is possible to perform further filtering of methods in the body //​[[CL:​Glossary:​form|forms]]//​ by using normal list-processing operations and the functions **[[CL:​Functions:​method-qualifiers]]** and **[[CL:​Functions:​invalid-method-error]]**. It is permissible to use **[[CL:​Special Operators:​setq]]** on the variables named in the method group specifiers and to bind additional variables. It is also possible to bypass the method group specifier mechanism and do everything in the body //​[[CL:​Glossary:​form|forms]]//​. This is accomplished by writing a single method group with **[[CL:​Types:​wildcard|*]]** as its only //​qualifier-pattern//;​ the variable is then bound to a //​[[CL:​Glossary:​list]]//​ of all of the //​[[CL:​Glossary:​applicable methods]]//,​ in most-specific-first order.
 +
 +The body //forms// compute and return the //​[[CL:​Glossary:​form]]//​ that specifies how the methods are combined, that is, the effective method. The effective method is evaluated in the //​[[CL:​Glossary:​null lexical environment]]//​ augmented with a local macro definition for **[[CL:​Functions:​call-method]]** and with bindings named by symbols not //​[[CL:​Glossary:​accessible]]//​ from the **common-lisp-user** //​[[CL:​Glossary:​package]]//​. Given a method object in one of the //​[[CL:​Glossary:​lists]]//​ produced by the method group specifiers and a //​[[CL:​Glossary:​list]]//​ of next methods, **[[CL:​Functions:​call-method]]** will invoke the method such that **[[CL:​Functions:​call-next-method]]** has available the next methods.
 +
 +When an effective method has no effect other than to call a single method, some implementations employ an optimization that uses the single method directly as the effective method, thus avoiding the need to create a new effective method. This optimization is active when the effective method form consists entirely of an invocation of the **[[CL:​Functions:​call-method]]** macro whose first //​[[CL:​Glossary:​subform]]//​ is a method object and whose second //​[[CL:​Glossary:​subform]]//​ is **[[CL:​Constant Variables:​nil]]** or unsupplied. Each **define-method-combination** body is responsible for stripping off redundant invocations of **[[CL:​Special Operators:​progn]]**,​ **[[CL:​Macros:​and]]**,​ **[[CL:​Macros:​multiple-value-prog1]]**,​ and the like, if this optimization is desired.
 +
 +The list **[[CL:​Functions:​(:​arguments . //​lambda-list//​)]]** can appear before any declarations or //​[[CL:​Glossary:​documentation string]]//. This form is useful when the //​[[CL:​Glossary:​method combination]]//​ type performs some specific behavior as part of the combined method and that behavior needs access to the arguments to the //​[[CL:​Glossary:​generic function]]//​. Each parameter variable defined by //​lambda-list//​ is bound to a //​[[CL:​Glossary:​form]]//​ that can be inserted into the effective method. When this //​[[CL:​Glossary:​form]]//​ is evaluated during execution of the effective method, its value is the corresponding argument to the //​[[CL:​Glossary:​generic function]]//;​ the consequences of using such a //​[[CL:​Glossary:​form]]//​ as the //place// in a **[[CL:​Macros:​setf]]** //​[[CL:​Glossary:​form]]//​ are undefined.
 +
 +Argument correspondence is computed by dividing the **'':​arguments''​** //​lambda-list//​ and the //​[[CL:​Glossary:​generic function]]//​ //​lambda-list//​ into three sections: the //​[[CL:​Glossary:​required parameter|required parameters]]//,​ the //​[[CL:​Glossary:​optional parameter|optional parameters]]//,​ and the //​[[CL:​Glossary:​keyword]]//​ and //​[[CL:​Glossary:​rest parameters]]//​. The //​[[CL:​Glossary:​argument|arguments]]//​ supplied to the //​[[CL:​Glossary:​generic function]]//​ for a particular //​[[CL:​Glossary:​call]]//​ are also divided into three sections; the required //​[[CL:​Glossary:​argument|arguments]]//​ section contains as many //​[[CL:​Glossary:​argument|arguments]]//​ as the //​[[CL:​Glossary:​generic function]]//​ has //​[[CL:​Glossary:​required parameter|required parameters]]//,​ the optional //​[[CL:​Glossary:​argument|arguments]]//​ section contains as many arguments as the //​[[CL:​Glossary:​generic function]]//​ has //​[[CL:​Glossary:​optional parameter|optional parameters]]//,​ and the keyword/​rest //​[[CL:​Glossary:​argument|arguments]]//​ section contains the remaining arguments. Each //​[[CL:​Glossary:​parameter]]//​ in the required and optional sections of the **'':​arguments''​** //​lambda-list//​ accesses the argument at the same position in the corresponding section of the //​[[CL:​Glossary:​argument|arguments]]//​. If the section of the **'':​arguments''​** //​lambda-list//​ is shorter, extra //​[[CL:​Glossary:​argument|arguments]]//​ are ignored. If the section of the **'':​arguments''​** //​lambda-list//​ is longer, excess //​[[CL:​Glossary:​required parameter|required parameters]]//​ are bound to forms that evaluate to **[[CL:​Constant Variables:​nil]]** and excess //​[[CL:​Glossary:​optional parameter|optional parameters]]//​ are //​[[CL:​Glossary:​bound]]//​ to their initforms. The //​[[CL:​Glossary:​keyword parameters]]//​ and //​[[CL:​Glossary:​rest parameters]]//​ in the **'':​arguments''​** //​lambda-list//​ access the keyword/​rest section of the //​[[CL:​Glossary:​argument|arguments]]//​. If the **'':​arguments''​** //​lambda-list//​ contains **''&​key''​**,​ it behaves as if it also contained **''&​allow-other-keys''​**.
 +
 +In addition, **''&​whole''​** //var// can be placed first in the **'':​arguments''​** //​lambda-list//​. It causes //var// to be //​[[CL:​Glossary:​bound]]//​ to a //​[[CL:​Glossary:​form]]//​ that //​[[CL:​Glossary:​evaluate|evaluates]]//​ to a //​[[CL:​Glossary:​list]]//​ of all of the //​[[CL:​Glossary:​argument|arguments]]//​ supplied to the //​[[CL:​Glossary:​generic function]]//​. This is different from **''&​rest''​** because it accesses all of the arguments, not just the keyword/​rest //​[[CL:​Glossary:​argument|arguments]]//​.
 +
 +Erroneous conditions detected by the body should be reported with **[[CL:​Functions:​method-combination-error]]** or **[[CL:​Functions:​invalid-method-error]]**;​ these //​[[CL:​Glossary:​functions]]//​ add any necessary contextual information to the error message and will signal the appropriate error.
 +
 +The body //forms// are evaluated inside of the //​[[CL:​Glossary:​bindings]]//​ created by the //​[[CL:​Glossary:​lambda list]]// and method group specifiers. Declarations at the head of the body are positioned directly inside of //​[[CL:​Glossary:​bindings]]//​ created by the //​[[CL:​Glossary:​lambda list]]// and outside of the //​[[CL:​Glossary:​bindings]]//​ of the method group variables. Thus method group variables cannot be declared in this way. **[[CL:​Special Operators:​locally]]** may be used around the body, however.
 +
 +Within the body //forms//, //​generic-function-symbol//​ is bound to the //​[[CL:​Glossary:​generic function]]//​ //​[[CL:​Glossary:​object]]//​.
 +
 +//​documentation//​ is attached as a //​[[CL:​Glossary:​documentation string]]// to //name// (as kind **[[CL:​Types:​method-combination]]**) and to the //​[[CL:​Glossary:​method combination]]//​ //​[[CL:​Glossary:​object]]//​.
 +
 +Note that two methods with identical specializers,​ but with different //​[[CL:​Glossary:​qualifier|qualifiers]]//,​ are not ordered by the algorithm described in Step 2 of the method selection and combination process described in \secref\MethodSelectionAndCombination. Normally the two methods play different roles in the effective method because they have different //​[[CL:​Glossary:​qualifier|qualifiers]]//,​ and no matter how they are ordered in the result of Step 2, the effective method is the same. If the two methods play the same role and their order matters, ​ an error is signaled. This happens as part of the //​[[CL:​Glossary:​qualifier]]//​ pattern matching in **define-method-combination**.
 +
 +---------
 +
 +If a **define-method-combination** //​[[CL:​Glossary:​form]]//​ appears as a //​[[CL:​Glossary:​top level form]]//, the //​[[CL:​Glossary:​compiler]]//​ must make the //​[[CL:​Glossary:​method combination]]//​ //​[[CL:​Glossary:​name]]//​ be recognized as a valid //​[[CL:​Glossary:​method combination]]//​ //​[[CL:​Glossary:​name]]//​ in subsequent **[[CL:​Macros:​defgeneric]]** //​[[CL:​Glossary:​form|forms]]//​. However, the //​[[CL:​Glossary:​method combination]]//​ is executed no earlier than when the **define-method-combination** //​[[CL:​Glossary:​form]]//​ is executed, and possibly as late as the time that //​[[CL:​Glossary:​generic function|generic functions]]//​ that use the //​[[CL:​Glossary:​method combination]]//​ are executed.
 +
 +====Examples====
 +Most examples of the long form of **define-method-combination** also illustrate the use of the related //​[[CL:​Glossary:​functions]]//​ that are provided as part of the declarative //​[[CL:​Glossary:​method combination]]//​ facility.
 +
 +<​blockquote>​
 +;;; TODO: Lock on package COMMON-LISP violated when defining AND
 +;;;       as a method combination while in package COMMON-LISP-USER.
 +</​blockquote>​
 +
 +===Examples of the short form of define-method-combination===
 +<​blockquote>​
 +(define-method-combination [[CL:​Macros:​and]] :​identity-with-one-argument [[CL:​Constant Variables:​t]])
 +
 +([[CL:​Macros:​defmethod]] func [[CL:​Macros:​and]] ((x class1) y) 
 +  ...)
 +</​blockquote>​
 +
 +The equivalent of this example in the long form is:
 +
 +<​blockquote>​
 +(define-method-combination [[CL:​Macros:​and]] ​
 +    (&​optional (order :​most-specific-first))
 +    ((around (:​around)) ​
 +     ​(primary ([[CL:​Macros:​and]]) :order order :required [[CL:​Constant Variables:​t]]))
 +  ([[CL:​Special Operators:​let]] ((form ([[CL:​Special Operators:​if]] (rest primary) ​
 +                  `([[CL:​Macros:​and]] ,​@([[CL:​Functions:​mapcar]] #'​([[CL:​Symbols:​lambda]] (method) ​
 +                                      `([[CL:​Macros:​call-method]] ,​method)) ​
 +                                  primary))
 +                  `([[CL:​Macros:​call-method]] ,​([[CL:​Functions:​first]] primary)))))
 +    ([[CL:​Special Operators:​if]] around ​
 +        `([[CL:​Macros:​call-method]] ,​([[CL:​Functions:​first]] around) ​
 +                      (,​@([[CL:​Functions:​rest]] around) ​
 +                       ​([[CL:​Macros:​make-method]] ,form)))
 +        form)))
 +</​blockquote>​
 +
 +===Examples of the long form of define-method-combination===
 +The default //​[[CL:​Glossary:​method combination]]//​ technique.
 +
 +<​blockquote>​
 +(define-method-combination standard ()
 +    ((around (:​around)) ​
 +     ​(before (:​before)) ​
 +     ​(primary () :required t)
 +     ​(after (:after)))
 +  ([[CL:​Special Operators:​flet]] ((call-methods (methods) ​
 +           ​([[CL:​Functions:​mapcar]] #'​([[CL:​Symbols:​lambda]] (method) ​
 +                       ​`([[CL:​Macros:​call-method]] ,​method)) ​
 +                   ​methods)))
 +    ([[CL:​Special Operators:​let]] ((form ([[CL:​Special Operators:​if]] ([[CL:​Macros:​or]] before after ([[CL:​Functions:​rest]] primary))
 +                    `([[CL:​Special Operators:​multiple-value-prog1]] ​
 +                       ​([[CL:​Special Operators:​progn]] ,​@(call-methods before)
 +                         ​([[CL:​Macros:​call-method]] ,​([[CL:​Functions:​first]] primary) ​
 +                                      ,​([[CL:​Functions:​rest]] primary)))
 +                         ,​@(call-methods ([[CL:​Functions:​reverse]] after)))
 +                    `([[CL:​Macros:​call-method]] ,​([[CL:​Functions:​first]] primary)))))
 +      ([[CL:​Special Operators:​if]] around ​
 +          `([[CL:​Macros:​call-method]] ,​([[CL:​Functions:​first]] around) ​
 +                        (,​@([[CL:​Functions:​rest]] around)
 +                         ​([[CL:​Macros:​make-method]] ,form)))
 +          form))))
 +</​blockquote>​
 +
 +A simple way to try several //​[[CL:​Glossary:​methods]]//​ until one returns //​[[CL:​Glossary:​non-nil]]//​.
 +<​blockquote>​
 +(define-method-combination [[CL:​Macros:​or]] () 
 +    ((methods ([[CL:​Macros:​or]])))
 +  `([[CL:​Macros:​or]] ,​@([[CL:​Functions:​mapcar]] #'​([[CL:​Symbols:​lambda]] (method) ​
 +                     ​`([[CL:​Macros:​call-method]] ,​method)) ​
 +                 ​methods)))
 +</​blockquote>​
 +
 +A more complete version of the preceding.
 +<​blockquote>​
 +(define-method-combination [[CL:​Macros:​or]] ​
 +    (&​optional (order ':​most-specific-first)) ​
 +    ((around (:​around)) ​
 +     ​(primary ([[CL:​Macros:​or]])))
 +  ;; Process the order argument ​
 +  ([[CL:​Macros:​case]] order 
 +    (:​most-specific-first) ​
 +    (:​most-specific-last ([[CL:​Macros:​setf]] primary ([[CL:​Functions:​reverse]] primary)))
 +    (otherwise ([[CL:​Functions:​method-combination-error]] "~S is an invalid order.~@ ​
 +    :​most-specific-first and :​most-specific-last are the possible values." ​
 +                                         ​order)))
 +  ;; Must have a primary method ​
 +  ([[CL:​Macros:​unless]] primary ​
 +    ([[CL:​Functions:​method-combination-error]] "A primary method is required."​)) ​
 +  ;; Construct the form that calls the primary methods
 +  ([[CL:​Special Operators:​let]] ((form ([[CL:​Special Operators:​if]] ([[CL:​Functions:​rest]] primary) ​
 +                  `([[CL:​Macros:​or]] ,​@([[CL:​Functions:​mapcar]] #'​([[CL:​Symbols:​lambda]] (method) ​
 +                                     ​`([[CL:​Macros:​call-method]] ,​method)) ​
 +                                 ​primary))
 +                  `([[CL:​Macros:​call-method]] ,​([[CL:​Functions:​first]] primary)))))
 +    ;; Wrap the around methods around that form 
 +    ([[CL:​Special Operators:​if]] around ​
 +        `([[CL:​Macros:​call-method]] ,​([[CL:​Functions:​first]] around) ​
 +                      (,​@([[CL:​Functions:​rest]] around) ​
 +                       ​([[CL:​Macros:​make-method]] ,​form))) ​
 +        form)))
 +</​blockquote>​
 +
 +The same thing, using the :order and :required keyword options.
 +<​blockquote>​
 +(define-method-combination [[CL:​Macros:​or]] ​
 +    (&​optional (order ':​most-specific-first)) ​
 +    ((around (:​around)) ​
 +     ​(primary ([[CL:​Macros:​or]]) :order order :required t))
 +  ([[CL:​Special Operators:​let]] ((form ([[CL:​Special Operators:​if]] ([[CL:​Functions:​rest]] primary)
 +                  `([[CL:​Macros:​or]] ,​@([[CL:​Functions:​mapcar]] #'​([[CL:​Symbols:​lambda]] (method) ​
 +                                     ​`([[CL:​Macros:​call-method]] ,method))
 +                                 ​primary))
 +                  `([[CL:​Macros:​call-method]] ,​([[CL:​Functions:​first]] primary)))))
 +    ([[CL:​Special Operators:​if]] around ​
 +        `([[CL:​Macros:​call-method]] ,​([[CL:​Functions:​first]] around) ​
 +                      (,​@([[CL:​Functions:​rest]] around) ​
 +                       ​([[CL:​Macros:​make-method]] ,form)))
 +        form)))
 +</​blockquote>​
 +
 +This short-form call is behaviorally identical to the preceding.
 +
 +<​blockquote>​
 +(define-method-combination [[CL:​Macros:​or]] :​identity-with-one-argument [[CL:​Constant Variables:​t]])
 +</​blockquote>​
 +
 +Order methods by positive integer qualifiers - ''​**:​around**''​ methods are disallowed to keep the example small.
 +
 +<​blockquote>​
 +(define-method-combination example-method-combination () 
 +    ((methods positive-integer-qualifier-p)) ​
 +  `([[CL:​Special Operators:​progn]] ​
 +     ,​@([[CL:​Functions:​mapcar]] #'​([[CL:​Symbols:​lambda]] (method) ​
 +                   ​`([[CL:​Macros:​call-method]] ,method))
 +               ​([[CL:​Functions:​stable-sort]] methods #'​[[CL:​Functions:​math-less|<​]]
 +                 :key #'​([[CL:​Symbols:​lambda]] (method) ​
 +                          ([[CL:​Functions:​first]] ([[CL:​Functions:​method-qualifiers]] method)))))))
 +
 +([[CL:​Macros:​defun]] positive-integer-qualifier-p (method-qualifiers)
 +  ([[CL:​Macros:​and]] ([[CL:​Functions:​math-equal|=]] ([[CL:​Functions:​length]] method-qualifiers) 1) 
 +       ​([[CL:​Functions:​typep]] ([[CL:​Functions:​first]] method-qualifiers) '​([[CL:​Types:​integer]] 0 [[CL:​Types:​wildcard|*]]))))
 +</​blockquote>​
 +
 +Example of the use of **'':​arguments''​**.
 +
 +<​blockquote>​
 +(define-method-combination progn-with-lock () 
 +    ((methods ())) 
 +  (:arguments object) ​
 +  `([[CL:​Special Operators:​unwind-protect]] ​
 +       ​([[CL:​Special Operators:​progn]] (lock (object-lock ,object))
 +              ,​@([[CL:​Functions:​mapcar]] #'​([[CL:​Special Operators:​lambda]] (method) ​
 +                            `([[CL:​Macros:​call-method]] ,​method)) ​
 +                        methods)) ​
 +     ​(unlock (object-lock ,object))))
 +</​blockquote>​
 +
 +====Affected By====
 +None.
 +
 +====Side Effects====
 +The //​[[CL:​Glossary:​compiler]]//​ is not required to perform any compile-time side-effects.
 +
 +====Exceptional Situations====
 +Method combination types defined with the short form require exactly one //​[[CL:​Glossary:​qualifier]]//​ per method. An error of type **[[CL:​Types:​error]]** is signaled if there are applicable methods with no //​[[CL:​Glossary:​qualifier|qualifiers]]//​ or with //​[[CL:​Glossary:​qualifier|qualifiers]]//​ that are not supported by the //​[[CL:​Glossary:​method combination]]//​ type. At least one primary method must be applicable or an error of type **[[CL:​Types:​error]]** is signaled.
 +
 +If an applicable method does not fall into any method group, the system signals an error of type **[[CL:​Types:​error]]** indicating that the method is invalid for the kind of //​[[CL:​Glossary:​method combination]]//​ in use.
 +
 +If the value of the **'':​required''​** option is //​[[CL:​Glossary:​true]]//​ and the method group is empty (that is, no applicable methods match the //​[[CL:​Glossary:​qualifier]]//​ patterns or satisfy the predicate), an error of type **[[CL:​Types:​error]]** is signaled.
 +
 +If the **'':​order''​** option evaluates to a value other than **'':​most-specific-first''​** or **'':​most-specific-last''​**,​ an error of type **[[CL:​Types:​error]]** is signaled.
 +
 +====See Also====
 +  * **[[CL:​Macros:​call-method|Macro CALL-METHOD]]**
 +  * **[[CL:​Functions:​call-next-method|Function CALL-NEXT-METHOD]]**
 +  * **[[CL:​Functions:​documentation|Generic Function DOCUMENTATION]]**
 +  * **[[CL:​Functions:​method-qualifiers|Generic Function METHOD-QUALIFIERS]]**
 +  * **[[CL:​Functions:​method-combination-error|Function METHOD-COMBINATION-ERROR]]**
 +  * **[[CL:​Functions:​invalid-method-error|Function INVALID-METHOD-ERROR]]**
 +  * **[[CL:​Macros:​defgeneric|Macro DEFGENERIC]]**
 +  * {\secref\MethodSelectionAndCombination}
 +  * {\secref\BuiltInMethCombTypes}
 +  * {\secref\DocVsDecls}
 +
 +====Notes====
 +The **'':​method-combination''​** option of **[[CL:​Macros:​defgeneric]]** is used to specify that a //​[[CL:​Glossary:​generic function]]//​ should use a particular //​[[CL:​Glossary:​method combination]]//​ type. The first argument to the **'':​method-combination''​** option is the //​[[CL:​Glossary:​name]]//​ of a //​[[CL:​Glossary:​method combination]]//​ type and the remaining arguments are options for that type.
 +
 +\issue{COMPILE-FILE-HANDLING-OF-TOP-LEVEL-FORMS:​CLARIFY} \issue{DECLS-AND-DOC} \issue{METHOD-COMBINATION-ARGUMENTS:​CLARIFY} \issue{DOCUMENTATION-FUNCTION-BUGS:​FIX} \issue{DEFINE-METHOD-COMBINATION:​CLARIFY}