Generic functions and methods

Neither nor CLOS use the message mechanism for methods as most Object Oriented language do. Instead, they use the notion of generic function. A generic function can be seen as a methods ``tanker''. When the evaluator requestd 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 funtion 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:
\begin{scheme}
(define-generic M)
(define-method M((a <integer>) b) 'integer)
(define-method M((a <real>) b) 'real)
(define-method M(a b) 'top)
\end{scheme}

The define-generic call defines M 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 M 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 speciliazer is given, the system defaults it to <top>. Thus, the first method definition is equivalent to


\begin{scheme}
(define-method M((a <integer>) (b <top>)) 'integer)
\end{scheme}

Now, let us look at some possible calls to generic function M:
\begin{scheme}
(M 2 3) \lev integer
(M 2 \schtrue) \lev integer
(M 1.2 'a) \lev ...
... top
(M 1 2 3) \lev error (since no method exists for 3 parameters)
\end{scheme}

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
\begin{scheme}
(define-method M ((a <integer>) (b <number>)) 'integer-number)
(d...
...al>)) 'integer-real)
(define-method M (a (b <number>)) 'top-number)
\end{scheme}

In this case,
\begin{scheme}
(M 1 2) \lev integer-integer
(M 1 1.0) \lev integer-real
(M 1 {\schtrue}) \lev integer
(M 'a 1) \lev 'top-number
\end{scheme}