8.7.1 Class Precedence List

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>) 

Footnotes

(34)

This section is an adaptation of material from Jeff Dalton’s (J.Dalton@ed.ac.uk) Brief introduction to CLOS