GOOPS generic functions and accessors often have short, generic names.
For example, if a vector package provides an accessor for the X
coordinate of a vector, that accessor may just be called x
. It
doesn’t need to be called, for example, vector:x
, because
GOOPS will work out, when it sees code like (x obj)
, that
the vector-specific method of x
should be called if obj is
a vector.
That raises the question, though, of what happens when different
packages define a generic function with the same name. Suppose we work
with a graphical package which needs to use two independent vector
packages for 2D and 3D vectors respectively. If both packages export
x
, what does the code using those packages end up with?
duplicate binding handlers explains how
this is resolved for conflicting bindings in general. For generics,
there is a special duplicates handler, merge-generics
, which
tells the module system to merge generic functions with the same name.
Here is an example:
(define-module (math 2D-vectors) #:use-module (oop goops) #:export (x y ...)) (define-module (math 3D-vectors) #:use-module (oop goops) #:export (x y z ...)) (define-module (my-module) #:use-module (oop goops) #:use-module (math 2D-vectors) #:use-module (math 3D-vectors) #:duplicates (merge-generics))
The generic function x
in (my-module)
will now incorporate
all of the methods of x
from both imported modules.
To be precise, there will now be three distinct generic functions named
x
: x
in (math 2D-vectors)
, x
in (math
3D-vectors)
, and x
in (my-module)
; and these functions
share their methods in an interesting and dynamic way.
To explain, let’s call the imported generic functions (in (math
2D-vectors)
and (math 3D-vectors)
) the ancestors, and the
merged generic function (in (my-module)
), the descendant.
The general rule is that for any generic function G, the applicable
methods are selected from the union of the methods of G’s descendant
functions, the methods of G itself and the methods of G’s ancestor
functions.
Thus ancestor functions effectively share methods with their
descendants, and vice versa. In the example above, x
in
(math 2D-vectors)
will share the methods of x
in
(my-module)
and vice versa.32 Sharing is
dynamic, so adding another new method to a descendant implies adding it
to that descendant’s ancestors too.
But note that x
in
(math 2D-vectors)
doesn’t share methods with x
in
(math 3D-vectors)
, so modularity is still preserved.