Defining Generic Functions and Methods


defgeneric : defining form

Syntax

defgeneric form = '(', 'defgeneric', gf name, gf lambda list, {level 0 init option}, ')'; gf name = identifier; (* ?? *) gf lambda list = specialized lambda list; level 0 init option = 'method:', method description; method description = '(', specialized lambda list, {form}, ')'; specialized lambda list = '(', specialized parameter, {specialized parameter}, ['.', identifier], ')'; (* ?? *) specialized parameter = '(', identifier, class name, ')' (* ?? *) | identifier; (* ?? *)

Arguments

gf name
One of a symbol, a form denoting a setter function or a converter function.
gf lambda list
The parameter list of the generic function, which may be specialized to restrict the domain of methods to be attached to the generic function.
level 0 init option
A sequence of options as specified below.

Remarks

This defining form defines a new generic function. The resulting generic function will be bound to gf name. The second argument is the formal parameter list. The method's specialized lamba list must be congruent to that of the generic function. Two lambda lists are said to be congruent iff:
  1. both have the same number of formal parameters, and
  2. if one lambda list has a rest formal parameter then the other lambda list has a rest formal parameter too, and vice versa.
An error is signalled (condition class: <non-congruent-lambda-lists>) if any method defined on this generic function does not have a lambda list congruent to that of the generic function.

An error is signalled (condition class: <incompatible-method-domain>) if the method's specialized lambda list widens the domain of the generic function. In other words, the lambda lists of all methods must specialize on subclasses of the classes in the lambda list of the generic function.

An error is signalled (condition class: <method-domain-clash>) if any methods defined on this generic function have the same domain. These conditions apply both to methods defined at the same time as the generic function and to any methods added subsequently by codedefmethod. An init-option is an identifier followed by a corresponding value.

An error is signalled (condition class: <no-applicable-method>) if an attempt is made to apply a generic function which has no applicable methods for the classes of the arguments supplied.

The level 0 init option is:

method : method-spec
This option is followed by a method description. A method description is a list comprising the specialized lambda list of the method, which denotes the domain, and a sequence of forms, denoting the method body. The method body is closed in the lexical environment in which the generic function definition appears. This option may be specified more than once.
The rewrite rules for defgeneric are: (defgeneric identifier gf-lambda-list level-0-init-option) => (defconstant identifier (generic-lambda gf-lambda-list level-0-init-option)) (defgeneric (setter identifier) gf-lambda-list level-0-init-option) => ((setter setter) identifier (generic-lambda gf-lambda-list level-0-init-option)) (defgeneric (converter identifier) gf-lambda-list level-0-init-option) => ((setter converter) identifier (generic-lambda gf-lambda-list level-0-init-option))

Examples

In the following example of the use of defgeneric a generic function named gf-0 is defined with three methods attached to it. The domain of gf-0 is constrained to be <object> x <class-a>. In consequence, each method added to the generic function, both here and later (by defmethod), must have a domain which is a subclass of <object> x <class-a>, which is to say that <class-c>, <class-e> and <class-g> must all be subclasses of <class-a>. (defgeneric gf-0 (arg1 (arg2 )) method: (((m1-arg1 ) (m1-arg2 )) ...) method: (((m2-arg1 ) (m2-arg2 )) ...) method: (((m3-arg1 ) (m3-arg2 )) ...))

See also:

defmethod, generic-lambda.


defmethod : macro

Syntax

defmethod form = '(', 'defmethod', gf specifier, specialized lambda list, {form}, ')'; gf specifier = identifier | '(', 'setter', identifier, ')' | '(', 'converter', identifier, ')';

Remarks

This macro is used for defining new methods on generic functions. A new method object is defined with the specified body and with the domain given by the specialized lambda list. This method is added to the generic function bound to the specified generic function. If the specialized-lambda-list is not congruent with that of the generic function, an error is signalled (condition class: <non-congruent-lambda-lists>). An error is signalled (condition class: <incompatible-method-domain>) if the method's specialized lambda list would widen the domain of the generic function. If there is a method with the same domain already defined on this generic function, an error is signalled (condition class: <method-domain-clash>).


generic-lambda : macro

Syntax

generic lambda form = '(', 'generic-lambda', gf lambda list, {level 0 init option}, ')';

Remarks

generic-lambda creates and returns an anonymous generic function that can be applied immediately, much like the normal lambda. The gen lambda list and the level 0 init options are interpreted exactly as for the level-0 definition of defgeneric.

Examples

In the following example an anonymous version of gf-0 (see defgeneric above) is defined. In all other respects the resulting object is the same as gf-0. (generic-lambda ((arg1 ) (arg2 )) method: (((m1-arg1 ) (m1-arg2 )) ...) method: (((m2-arg1 ) (m2-arg2 )) ...) method: (((m3-arg1 ) (m3-arg2 )) ...))

See also:

defgeneric.

Specializing Methods

The following two operators are used to specialize more general methods. The more specialized method can do some additional computation before calling these operators and can then carry out further computation before returning. It is an error to use either of these operators outside a method body. Argument bindings inside methods are immutable. Therefore an argument inside a method retains its specialized class throughout the processing of the method.


call-next-method : special form

Syntax

(call-next-method)

Result

The result of calling the next most specific applicable method.

Remarks

The next most specific applicable method is called with the same arguments as the current method. An error is signalled (condition class: <no-next-method>) if there is no next most specific method.


next-method-p : special form

Syntax

(next-method-p)

Result

If there is a next most specific method, next-method-p returns a non-() value, otherwise, it returns ().

Method Lookup and Generic Dispatch

The system defined method lookup and generic function dispatch is purely class based. eql methods known from CLOS are excluded. The application behaviour of a generic function can be described in terms of method lookup and generic dispatch. The method lookup determines
  1. which methods attached to the generic function are applicable to the supplied arguments, and
  2. the linear order of the applicable methods with respect to classes of the arguments and the argument precedence order.
A class C1 is called more specific than class C2 with respect to C3 iff C1 appears before C2 in the class precedence list (CPL) of C3 (Note 1: This definition is required when multiple inheritance comes into play. Then, two classes have to be compared with respect to a third class even if they are not related to each other via the subclass relationship. Although, multiple inheritance is not provided at level-0, the method lookup protocol is independent of the inheritance strategy defined on classes. It depends on the class precedence lists of the domains of methods attached to the generic function and the argument classes involved.).

Two additional concepts are needed to explain the processes of method lookup and generic dispatch: (i) whether a method is applicable, (ii) how specific it is in relation to the other applicable methods. The definitions of each of these terms is now given.

A method with the domain D-1 x ... x D-m x <list> is applicable to the arguments a-1 ... a-m [a-m+1 ... a-n] if the class of each argument, C-i, is a subclass of D-i, which is to say, D-i is a member of C-i's class precedence list.

A method M-1 with the domain D-11 x ... x D-1m [ x <list>] is more specific than a method M-2 with the domain D-21 x ... x D-2m [ x <list>] with respect to the arguments a-1 ... a-m [a-m+1 ... a-n] iff there exists an i in 1 ... m such that D-1i is more specific than D-2i with respect to C-i, the class of a-i, and for all j=1 ... i-1, D-2j is not more specific than D-1j with respect to C-j, the class of a-j.

Now, with the above definitions, we can describe the application behaviour of a generic function (f a-1 ... a-m [a-m+1 ... a-n]):

  1. Select the methods applicable to a-1 ... a-m [a-m+1 ... a-n] from all methods attached to f.
  2. Sort the applicable methods M-1 ...M-k into decreasing order of specificity using left to right argument precedence order to resolve otherwise equally specific methods.
  3. If call-next-method appears in one of the method bodies, make the sorted list of applicable methods available for it.
  4. Apply the most specific method on a-1 ... a-m [a-m+1 ... a-n].
  5. Return the result of the previous step.
The first two steps are usually called method lookup and the first four are usually called generic dispatch.
Julian Padget, jap@maths.bath.ac.uk, this version February 27, 1995