Node:Generic functions and methods, Next:, Previous:Generic functions, Up:Generic functions



5.5.1 Generic functions and methods

Neither GOOPS nor CLOS use the message mechanism for methods as most Object Oriented language do. Instead, they use the notion of generic functions. A generic function can be seen as a methods "tanker". When the evaluator requested the application of a generic function, all the methods of this generic function will be grabbed and the most specific among them will be applied. We say that a method M is more specific than a method M' if the class of its parameters are more specific than the M' ones. To be more precise, when a generic function must be "called" the system will:

  1. search among all the generic function those which are applicable
  2. sort the list of applicable methods in the "most specific" order
  3. call the most specific method of this list (i.e. the first method of the sorted methods list).

The definition of a generic function is done with the define-generic macro. Definition of a new method is done with the define-method macro. Note that define-method automatically defines the generic function if it has not been defined before. Consequently, most of the time, the define-generic needs not be used. Consider the following definitions:

(define-generic G)
(define-method  (G (a <integer>) b) 'integer)
(define-method  (G (a <real>) b) 'real)
(define-method  (G a b) 'top)

The define-generic call defines G as a generic function. Note that the signature of the generic function is not given upon definition, contrarily to CLOS. This will permit methods with different signatures for a given generic function, as we shall see later. The three next lines define methods for the G generic function. Each method uses a sequence of parameter specializers that specify when the given method is applicable. A specializer permits to indicate the class a parameter must belong to (directly or indirectly) to be applicable. If no specializer is given, the system defaults it to <top>. Thus, the first method definition is equivalent to

(define-method (G (a <integer>) (b <top>)) 'integer)

Now, let us look at some possible calls to generic function G:

(G 2 3)    => integer
(G 2 #t)   => integer
(G 1.2 'a) => real
(G #t #f)  => top
(G 1 2 3)  => error (since no method exists for 3 parameters)

The preceding methods use only one specializer per parameter list. Of course, each parameter can use a specializer. In this case, the parameter list is scanned from left to right to determine the applicability of a method. Suppose we declare now

(define-method (G (a <integer>) (b <number>))  'integer-number)
(define-method (G (a <integer>) (b <real>))    'integer-real)
(define-method (G (a <integer>) (b <integer>)) 'integer-integer)
(define-method (G a (b <number>))              'top-number)

In this case,

(G 1 2)   => integer-integer
(G 1 1.0) => integer-real
(G 1 #t)  => integer
(G 'a 1)  => top-number