|
ANSI Common Lisp 7 Objects 7.7 Dictionary of Objects
7.7.33 define-method-combination |
Macro |
- 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
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
- Arguments and Values:
-
args-lambda-list -
a define-method-combination arguments lambda list.
declaration - a declare expression; not evaluated.
description - a format control.
documentation - a string; not evaluated.
forms - an implicit progn
that must compute and return the form that specifies how
the methods are combined, that is, the effective method.
generic-function-symbol - a symbol.
identity-with-one-argument - a generalized boolean.
lambda-list - ordinary lambda list.
name - a symbol.
Non-keyword, non-nil symbols are usually used.
operator - an operator.
Name and operator are often the same symbol.
This is the default, but it is not required.
order - :most-specific-first or :most-specific-last; evaluated.
predicate - a symbol that names a function of one argument
that returns a generalized boolean.
qualifier-pattern - a list,
or the symbol *.
required-p - a generalized boolean.
- Description:
-
The macro define-method-combination is used to define new types
of 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 defmacro in that the body is an
expression, usually using backquote, that computes a form. Thus
arbitrary control structures can be implemented. The long form also
allows arbitrary processing of method qualifiers.
- Short Form
The short form syntax of define-method-combination is recognized
when the second subform is a non-nil symbol or is not present.
When the short form is used, name is defined as a type of
method combination that produces a Lisp form
(operator method-call method-call ... ).
The operator is a symbol that can be the name of a
function, macro, or 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 true (the default is 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 function call. This option is designed to be
used with operators such as progn, and, +, and
max.
-
The :operator option specifies the name of the operator. The
operator argument is a symbol that can be the
name of a function,
macro, or
special form.
These types of method combination require exactly one qualifier per
method. An error is signaled if there are applicable methods with no
qualifiers or with qualifiers that are not supported by
the method combination type.
A method combination procedure defined in this way recognizes two
roles for methods. A method whose one qualifier is the symbol naming
this type of 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 qualifier is an auxiliary
method that behaves the same as an around method in standard
method combination. The function call-next-method can only be
used in around methods; it cannot be used in primary methods
defined by the short form of the define-method-combination macro.
A 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
around methods.
For a discussion of built-in method combination types,
see Section 7.6.6.4 Built-in Method Combination Types.
- Long Form
The long form syntax of define-method-combination is recognized
when the second subform is a list.
The lambda-list
receives any arguments provided after the name of the method
combination type in the :method-combination option to
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 qualifiers against some patterns or by testing their qualifiers with
a predicate.
These method group specifiers define all method qualifiers
that can be used with this type of method combination.
The car of each method-group-specifier is a symbol
which names a variable.
During the execution of
the forms in the body of define-method-combination, this
variable is bound to a list of the methods in the method group. The
methods in this list occur in the order specified by the
:order option.
If qualifier-pattern is a symbol it must be *.
A method matches
a qualifier-pattern if the method's
list of qualifiers is equal
to the qualifier-pattern (except that the symbol * in a
qualifier-pattern matches anything). Thus
a qualifier-pattern can be one of the
following:
the empty list, which matches unqualified methods;
the symbol *, which matches all methods;
a true list, which matches methods with the same number of qualifiers
as the length of the list when each qualifier matches
the corresponding list element; or
a dotted list that ends in the symbol *
(the * matches any number of additional 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 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 qualifier 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 symbol other than nil or *.
If there is an applicable method that does not fall into any method group,
the function invalid-method-error is called.
Method group specifiers can have keyword options following the
qualifier patterns or predicate. Keyword options can be distinguished from
additional qualifier patterns because they are neither lists nor the symbol
*. 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
(apply #'format stream format-control (method-qualifiers method))
to print this description, which
is expected to be concise. This keyword
option allows the description of a method qualifier to be defined in
the same module that defines the meaning of the
method qualifier. In most cases, format-control will not contain any
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 qualifier patterns and on whether
this method group includes the unqualified methods.
-
The :order option specifies the order of methods. The order
argument is a 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 true and the method group is empty
(that is, no applicable methods match the qualifier patterns
or satisfy the predicate),
an error is signaled.
If :required is not supplied,
it defaults to 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 forms by using normal list-processing operations
and the functions method-qualifiers and
invalid-method-error. It is permissible to use 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 forms. This is accomplished
by writing a single method group with * as its only
qualifier-pattern;
the variable is then bound to a list of all of the
applicable methods, in most-specific-first order.
The body forms compute and return the form that specifies
how the methods are combined, that is, the effective method.
The effective method is evaluated in
the null lexical environment augmented with a local macro definition
for call-method and with bindings named by
symbols not accessible from the common-lisp-user package.
Given a method object in one of the
lists produced by the method group
specifiers and a list of next methods,
call-method
will invoke the method such that 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 call-method macro whose first subform is a method object and
whose second subform is nil or unsupplied. Each
define-method-combination body is responsible for stripping off
redundant invocations of progn, and,
multiple-value-prog1, and the like, if this optimization is desired.
The list (:arguments . lambda-list) can appear before
any declarations or documentation string. This form is useful when
the method combination type performs some specific behavior as part of
the combined method and that behavior needs access to the arguments to
the generic function. Each parameter variable defined by
lambda-list is bound to a form that can be inserted into the
effective method. When this form is evaluated during execution of the
effective method, its value is the corresponding argument to the
generic function; the consequences of using such a form as
the place in a setf form are undefined.
Argument correspondence is computed by dividing the :arguments lambda-list
and the generic function lambda-list into three sections:
the required parameters,
the optional parameters,
and the keyword and rest parameters.
The arguments supplied to the generic function for a particular call
are also divided into three sections;
the required arguments section contains as many arguments
as the generic function has required parameters,
the optional arguments section contains as many arguments
as the generic function has optional parameters,
and the keyword/rest arguments section contains the remaining arguments.
Each 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 arguments.
If the section of the :arguments lambda-list is shorter,
extra arguments are ignored.
If the section of the :arguments lambda-list is longer,
excess required parameters are bound to forms that evaluate to nil
and excess optional parameters are bound to their initforms.
The keyword parameters and rest parameters in the :arguments
lambda-list access the keyword/rest section of the 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 bound to a form
that evaluates to a list of all of the arguments supplied
to the generic function. This is different from &rest because it
accesses all of the arguments, not just the keyword/rest arguments.
Erroneous conditions detected by the body should be reported with
method-combination-error or invalid-method-error; these
functions
add any necessary contextual information to the error message and will
signal the appropriate error.
The body forms are evaluated inside of the bindings created by
the
lambda list and method group specifiers.
Declarations at the head of
the body are positioned directly inside of bindings created by the
lambda list and outside of the bindings of the method group variables.
Thus method group variables cannot be declared in this way. locally may be used
around the body, however.
Within the body forms, generic-function-symbol
is bound to the generic function object.
Documentation is attached as a documentation string
to name (as kind method-combination)
and to the method combination object.
Note that two methods with identical specializers, but with different
qualifiers, are not ordered by the algorithm described in Step 2 of
the method selection and combination process described in
Section 7.6.6 Method Selection and Combination. Normally the two methods play
different roles in the effective method because they have different
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 qualifier pattern matching in
define-method-combination.
If a define-method-combination form appears as a
top level form, the compiler must make the
method combination name be recognized as a valid
method combination name in subsequent defgeneric
forms. However, the method combination is executed
no earlier than when the define-method-combination form
is executed, and possibly as late as the time that generic functions
that use the method combination are executed.
- Examples:
-
Most examples of the long form of define-method-combination also
illustrate the use of the related functions that are provided as part
of the declarative method combination facility.
;;; Examples of the short form of define-method-combination
(define-method-combination and :identity-with-one-argument t)
(defmethod func and ((x class1) y) ...)
;;; The equivalent of this example in the long form is:
(define-method-combination and
(&optional (order :most-specific-first))
((around (:around))
(primary (and) :order order :required t))
(let ((form (if (rest primary)
`(and ,@(mapcar #'(lambda (method)
`(call-method ,method))
primary))
`(call-method ,(first primary)))))
(if around
`(call-method ,(first around)
(,@(rest around)
(make-method ,form)))
form)))
;;; Examples of the long form of define-method-combination
;The default method-combination technique
(define-method-combination standard ()
((around (:around))
(before (:before))
(primary () :required t)
(after (:after)))
(flet ((call-methods (methods)
(mapcar #'(lambda (method)
`(call-method ,method))
methods)))
(let ((form (if (or before after (rest primary))
`(multiple-value-prog1
(progn ,@(call-methods before)
(call-method ,(first primary)
,(rest primary)))
,@(call-methods (reverse after)))
`(call-method ,(first primary)))))
(if around
`(call-method ,(first around)
(,@(rest around)
(make-method ,form)))
form))))
;A simple way to try several methods until one returns non-nil
(define-method-combination or ()
((methods (or)))
`(or ,@(mapcar #'(lambda (method)
`(call-method ,method))
methods)))
;A more complete version of the preceding
(define-method-combination or
(&optional (order ':most-specific-first))
((around (:around))
(primary (or)))
;; Process the order argument
(case order
(:most-specific-first)
(:most-specific-last (setq primary (reverse primary)))
(otherwise (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
(unless primary
(method-combination-error "A primary method is required."))
;; Construct the form that calls the primary methods
(let ((form (if (rest primary)
`(or ,@(mapcar #'(lambda (method)
`(call-method ,method))
primary))
`(call-method ,(first primary)))))
;; Wrap the around methods around that form
(if around
`(call-method ,(first around)
(,@(rest around)
(make-method ,form)))
form)))
;The same thing, using the :order and :required keyword options
(define-method-combination or
(&optional (order ':most-specific-first))
((around (:around))
(primary (or) :order order :required t))
(let ((form (if (rest primary)
`(or ,@(mapcar #'(lambda (method)
`(call-method ,method))
primary))
`(call-method ,(first primary)))))
(if around
`(call-method ,(first around)
(,@(rest around)
(make-method ,form)))
form)))
;This short-form call is behaviorally identical to the preceding
(define-method-combination or :identity-with-one-argument t)
;Order methods by positive integer qualifiers
;:around methods are disallowed to keep the example small
(define-method-combination example-method-combination ()
((methods positive-integer-qualifier-p))
`(progn ,@(mapcar #'(lambda (method)
`(call-method ,method))
(stable-sort methods #'<
:key #'(lambda (method)
(first (method-qualifiers method)))))))
(defun positive-integer-qualifier-p (method-qualifiers)
(and (= (length method-qualifiers) 1)
(typep (first method-qualifiers) '(integer 0 *))))
;;; Example of the use of :arguments
(define-method-combination progn-with-lock ()
((methods ()))
(:arguments object)
`(unwind-protect
(progn (lock (object-lock ,object))
,@(mapcar #'(lambda (method)
`(call-method ,method))
methods))
(unlock (object-lock ,object))))
- Side Effects:
-
The compiler is not required to perform any compile-time side-effects.
- Exceptional Situations:
-
Method combination types defined with the short form require exactly
one qualifier per method.
An error of type error is signaled if there are
applicable methods with no qualifiers or with qualifiers that are not
supported by the method combination type.
At least one primary method must be applicable or
an error of type error is signaled.
If an applicable method does not fall into any method group, the
system signals an error of type error
indicating that the method is invalid for the kind of
method combination in use.
If the value of the :required option is true
and the method group is empty (that is, no applicable
methods match the qualifier patterns or satisfy the predicate),
an error of type error is signaled.
If the :order option evaluates to a value other than
:most-specific-first or :most-specific-last,
an error of type error is signaled.
- See Also:
-
call-method,
call-next-method,
documentation,
method-qualifiers,
method-combination-error,
invalid-method-error,
defgeneric,
Section 7.6.6 Method Selection and Combination,
Section 7.6.6.4 Built-in Method Combination Types,
Section 3.4.11 Syntactic Interaction of Documentation Strings and Declarations
- Notes:
-
The :method-combination option of defgeneric is used to
specify that a generic function should use a particular method
combination type. The first argument to the :method-combination
option is the name of a method combination type and the remaining
arguments are options for that type.
- Allegro CL Implementation Details:
-
None.
|