[Sorry for the delay, but I haven’t seen this email before.]
On 13 Jan 2014, at 17:25, Robert P. Goldman rpgoldman@sift.info wrote:
Pascal Costanza wrote:
I need to look into a means to identify these problematic cases. Ugh.
MOP not standardized. Ugh ugh ugh ugh. I'll see if I can pilfer code from Closer-MOP to render ENSURE-CLASS accessible.
I wouldn’t recommend fiddling with ensure-class or ensure-class-using-class. It’s too hard to specialize methods correctly on those, and their portability across CL implementations is not very good.
If you want to implement checks on subclasses, it’s better to do this in initialize-instance / reinitialize-instance on metaclasses, but this requires that the subclasses also use those metaclasses, which may break other assumptions.
Sorry if this is a foolish question, but the class at issue here is OPERATION, whose metaclass is STANDARD-CLASS.
So am I correct in thinking I would need something like
(defmethod INITIALIZE-INSTANCE :BEFORE ((class standard-class) &key direct-superclasses) (when (member (find-class 'asdf:operation) direct-superclasses) ...handle my troublesome case...)))
where the primary portability challenge would be to make sure I have the right STANDARD-CLASS for the various implementations? [Hoping to cargo-cult that out of Closer-MOP.]
You are not allowed to define a method for a pre-defined generic function in the MOP on a pre-defined metaclass like this. At least one specializer must be one of your own metaclasses. (See “restrictions on portable programs” in the “Concepts” section of the CLOS MOP specification. It’s _very_ important to stick to these restrictions.)
So you’re only chance is to define an operation-class metaclass, and make sure that operation is an instance of operation-class (or similar).
Is this the moral of this story: code that wishes to be backwards compatible and portable can NEVER change the class hierarchy above an API-visible class, because there is no *portable* way to detect when someone's code will be affected.
The interface a class exposes to its subclasses is an interface like any other, and yes, interface changes may have negative consequences. That’s correct for any kind of interfaces. It can be better to create new interfaces and leave the existing ones untouched if backwards compatibility is a major concern.
I don't believe that it's true that a class exposes an interface like any other.
If my library exposes an API in the form of a function call, then I may easily catch any invocation of that function call and, e.g., provide a deprecation warning, etc.
Classes are essentially call-back interfaces. You would have similar issues if you would expose a higher-order function that starts to call the function parameters differently, and with different arguments. Especially, calling function parameters with different keyword arguments is very similar.
But I believe that your email indicates that we *cannot* portably identify cases where a user is extending a class that the library exposes.
Am I wrong about this? If so, please let me know! If not, I believe it shows that CLOS-based programs must be more cautious when exposing classes for extension, then when exposing other code artifacts.
Yes, you have to be more cautious with CLOS. In fact, this applies to OOP in general, not only CLOS.
Pascal
-- Pascal Costanza The views expressed in this email are my own, and not those of my employer.