Ian Bicking: the old part of his blog

Re: adaptation

Your criticism also sounds a little bit like a possible criticism of object oriented programming: "one-off objects that define no methods; objects that are composed out of multiple objects; classes that are only ever instantiated once, delegation instead of inheritance... That OO thing is taking one nice but small idea and just keeps using it too much till its worn out and confused."

Yeah, I could totally get behind that criticism too. Some of the OO mechanisms (like inheritance) have been overused, particularly in places where the mechanism is convenient but not really a proper metaphor for what's going on.

interfaces that define no methods; interfaces that are just names - I'm not sure whether there's a difference between the two? We tend to call these 'marker interfaces'. I'm not sure whether these are very much related to adaptation itself. Adapters typically need some methods on the object to adapt and methods to expose on the adapter itself. I think we may need to a higher level abstraction that describes what you do with these.

I think string names do okay here. I can imagine a more structured way of doing this too, like "marker = Marker('some_useful_identifier')". It's vaguely like an enum. There's other more compact ways to deal with this.

adaptation that works on multiple objects - I can see the criticism of complication. It's also quite powerful, and I think one can succesfully argue this as a general case of adaptation where adaptation of a single object is a special case. A view is an adaptation of an object and a request, for instance. This allows the system to support different types of view altogether (HTTP, WebDAV, etc), skins, and so on.

I think generic functions are probably a better for many cases of multiadapters. There's probably several things hanging out in there, but I'm not sure how many are really adaptation. I think adaptation is possible from multiple objects, insofar as the tuple of those objects represents an object of its own (a very light and unnamed object); I'm not sure that I believe that's how it really is working in practice. In other cases, I think Zope leans on adaptation to avoid what could be simple attribute access. E.g., get_controller(some_model_object).webdav_response(request)

adaptation that works on no object at all - we actually call these utilities and we have a special API for them. I think here the use of the adaptation machinery is truly an implementation detail. This is basically pluggable import by interface.

This just isn't adaptation at all, even slightly. It's a reasonable function, just not adaptation. Maybe you are adding an API that doesn't look like adaptation? What I've seen still does look like adaptation.

Incidentally, the equivalent thing in Paste is paste.registry, though I've come to dislike proxies and prefer just having simple get_whatever_kind_of_utility(), along with perhaps a get_whatever_kind_of_utility.register(threadlocal_implementation). It took me a while to figure out what utilities were, but after using different threadlocal things I definitely see the pattern. It could also be considered a dynamically-scoped variable (ala *some-variable* in Lisps). with: offers a syntax which works well for this pattern too; anyway, I'd like to see this filter up to a single Python-wide pattern.

adaptation in place of attributes - we call these annotations. I think the annotations pattern can be valuable when extending an application without having to modify its classes, though I think it shouldn't be overused.

Annotations are quite interesting, and I can see the utility of adaptation here for supporting multiple backends. Though things like "identity" are a little vague to me, but maybe that would be clearer if I looked more closely at the code.

There's more: views in Zope 3 are also a kind of adapter, and the event system is built on top of adapters as well.

Views are reasonable, I think, but I'm not sure. Probably more on that in reply to your other comment.

I'm not that comfortable with the event system, but I haven't seen any one event system that really feels right to me on the whole. It seems like a reasonable event system for Zope, but I don't think I'd be inclined to use it elsewhere. I would prefer an event system where events were named (as is traditional), rather than using interfaces or adaptability to indicate applicability of the event to a listener. That feels less clever to me, and less clever is more good. Though I suppose most events also imply a kind of interface, as most events include arguments, and the meaning of those arguments is a kind of interface for listeners.

Comment on adaptation
by Ian Bicking

Comments:

You say you can totally get behind that critique of OO. That doesn't mean you think people shouldn't use OO, right? Does it mean you think OO is a simple idea taken too far? Similar to the component architecture, you can use the mechanisms of OO to design things the wrong way with it. It takes experience to know that you shouldn't overuse inheritance, and experience to know that yes, inheritance is actually the easier way to deal with some problems after all and you shouldn't be avoiding it then. We're clearly still gaining experience with appropriate uses of the Zope 3 component architecture.

I'm not sure whether we're talking about the same thing, but to me 'adaptation of nothing at all' sounds like a utility lookup, and we have a getUtility() API for that and various ways to register utilities. Utility lookup is not adaptation, but is implemented on top of the same machinery as that gives us interface-based lookup and a generic pluggable registry. You seem to say that it's wrong to use this machinery as an implementation detail. Why? Perhaps because you haven't seen the utility specific APIs?

Basically Zope 3's component architecture is built on top of the interface machinery. Interfaces have several advantages above strings for lookup purposes - they support a notion of interface inheritance and they are API documentation. The adaptation machinery then provides for a system of registration and pluggability. You can suggest several different potentially simpler mechanisms instead to replace the various patterns, but are you don't sure you won't actually lose elegance and power in that simplification process?

The desire to have a generic lookup and registration machinery comes from our experience with pluggable applications in the Zope 2 context. We have found that we end up with a lot of registry and lookup systems there, all slightly different, and a lot of Python-based code that does all sorts of registration. A single explicit system for lookup and registration becomes very attractive with that experience.

I agree by the way that generic methods are interesting and we should be investigating how they fit in a Zope 3 context.

# Martijn Faassen