Hi,
In the CLHS page for ENSURE-GENERIC-FUNCTION, there is this paragraph:
"If function-name specifies a generic function that has a different
value for the :generic-function-class argument and if the new generic
function class is compatible[1] with the old, change-class is called to
change the class of the generic function; otherwise an error is
signaled.”
Unfortunately, the CLHS doesn’t specify what the default value for :generic-function-class is, or what happens when the user doesn’t pass a value for :generic-function-class.
The CLHS entry for defgeneric, however, seems to suggest that if you leave out the :generic-function-class option in the defgeneric macro form, and the already existing generic function has a class other than standard-generic-function, its class remains unchanged. (This I would find unfortunate, because it would mean that the textual form that is “in effect” and the actual object class can be different from each other.)
In the AMOP however, one can read the following.
From the page on ENSURE-GENERIC-FUNCTION:
"The behavior of this function is actually implemented by the generic
function ensure-generic-function-using-class. When
ensure-generic-function is called, it immediately calls
ensure-generic-function-using-class and returns that result as its own."
And then from the page on ENSURE-GENERIC-FUNCTION-USING-CLASS:
"If the class of the generic-function-or-nil argument is not the same as
the class specified by the :generic-function-class argument, an error is
signaled."
Let alone the fact that this should only apply when there actually is an
existing generic function, this seems to be in contradiction with the
Common Lisp standard, as it would explicitly forbid changing the class
of the generic function, even in a "compatible" way.
Comments?
I don’t remember the exact details anymore why I arrived to that conclusion, because it’s some time ago by now, but my conclusion and strong recommendation is to stay away from ensure-generic-function, ensure-generic-funciton-using-class, and also ensure-class and ensure-class-using-class. My interpretation of the various specs for these is that they were supposed to be convenience functions for “end users”, imitating the effects of the corresponding macro versions, but trying to deal with too many corner cases at the same time which require different solutions, which is why they have these contradictory requirements and effects.
As far as I can tell, these functions provide nothing that can’t otherwise be expressed using more low-level means, including make-instance, initialize-instance, reinitialize-instance and change-class, which are much more cleanly specified and require a lot less guessing.
Both CLOS and the CLOS MOP could benefit from one or two more passes of careful specification fixes to make them more consistent, but I guess that’s not going to happen. :-(
Footnotes:
[1] BTW, what does "compatible" mean ?
Well, the AMOP essentially specifies that generic function metaobject classes are always only compatible with themselves.
CLOS was accepted as part of ANSI CL long before there was reasonable draft version of the CLOS MOP available, so some of the forward references to the CLOS MOP spec that were added to CLtL2 survived in ANSI CL without double-checking them to make them compatible again with the then-exising AMOP book.
My guess is that it turned out that specifying what compatibility between generic function metaobject classes actually means was too hard, so they resorted to the easy way out. ;)
Pascal