What happens when a class inherits from two or more superclasses that have a slot with the same name but incompatible definitions — for example, different init values or slot allocations? We need a rule for deciding which slot definition the derived class ends up with, and this rule is provided by the class’s Class Precedence List.34
Another problem arises when invoking a generic function, and there is
more than one method that could apply to the call arguments. Here we
need a way of ordering the applicable methods, so that Guile knows which
method to use first, which to use next if that method calls
next-method
, and so on. One of the ingredients for this ordering
is determining, for each given call argument, which of the specializing
classes, from each applicable method’s definition, is the most specific
for that argument; and here again the class precedence list helps.
If inheritance was restricted such that each class could only have one superclass — which is known as single inheritance — class ordering would be easy. The rule would be simply that a subclass is considered more specific than its superclass.
With multiple inheritance, ordering is less obvious, and we have to impose an arbitrary rule to determine precedence. Suppose we have
(define-class X () (x #:init-value 1)) (define-class Y () (x #:init-value 2)) (define-class Z (X Y) (...))
Clearly the Z
class is more specific than X
or Y
,
for instances of Z
. But which is more specific out of X
and Y
— and hence, for the definitions above, which
#:init-value
will take effect when creating an instance of
Z
? The rule in GOOPS is that the superclasses listed earlier
are more specific than those listed later. Hence X
is more
specific than Y
, and the #:init-value
for slot x
in
instances of Z
will be 1.
Hence there is a linear ordering for a class and all its superclasses, from most specific to least specific, and this ordering is called the Class Precedence List of the class.
In fact the rules above are not quite enough to always determine a
unique order, but they give an idea of how things work. For example,
for the F
class shown in Figure 8.2, the class precedence
list is
(f d e a c b <object> <top>)
In cases where there is any ambiguity (like this one), it is a bad idea for programmers to rely on exactly what the order is. If the order for some superclasses is important, it can be expressed directly in the class definition.
The precedence list of a class can be obtained by calling
class-precedence-list
. This function returns a ordered list
whose first element is the most specific class. For instance:
(class-precedence-list B) ⇒ (#<<class> B 401b97c8> #<<class> <object> 401e4a10> #<<class> <top> 4026a9d8>)
Or for a more immediately readable result:
(map class-name (class-precedence-list B)) ⇒ (B <object> <top>)
This section is an adaptation of material from Jeff Dalton’s (J.Dalton@ed.ac.uk) Brief introduction to CLOS