[Python-Dev] PEP 246, redux (original) (raw)

Phillip J. Eby pje at telecommunity.com
Tue Jan 11 19:32:53 CET 2005


At 11:59 AM 1/11/05 +0100, Alex Martelli wrote:

On 2005 Jan 11, at 11:01, Alex Martelli wrote:

On 2005 Jan 10, at 18:59, Phillip J. Eby wrote:

At 12:43 PM 1/10/05 -0500, Phillip J. Eby wrote:

As a practical matter, all of the existing interface systems (Zope, PyProtocols, and even the defunct Twisted implementation) treat interface inheritance as guaranteeing substitutability for the base interface, and do so transitively.

An additional data point, by the way: the Eclipse Java IDE has an adaptation system that works very much like PEP 246 does, and it appears that in a future release they intend to support automatic adapter transitivity, so as to avoid requiring each provider of an interface to "provide O(n^2) adapters when writing the nth version of an interface." IOW, their current release is transitive only for interface inheritance ala Zope or Twisted; their future release will be transitive for adapter chains ala PyProtocols. This is definitely relevant prior art, so thanks for pointing it out. If interfaces change so often that 'n' can become worryingly high, this is a valid concern. In my world, though, published interfaces do NOT change as often as to require such remedies;-).

FWIW, I don't believe that by "nth version" the original author wasn't referring to changed versions of the same interface, but was instead maybe trying to say that N interfaces that adapt to IFoo, when IFoo has M interfaces that it can be adapted to, means that N*M adapters are required in all, if adapter composition isn't possible.

"adapters" for a single underlying "object", is, I fear, stronger than we can impose (so it may be that Eclipse is a better parallel, but I know little of it while COM is in my bones, so that's what I keep thinking of;-).

Fair enough. I think Eclipse's implementation maps fairly directly onto PEP 246, except that conform is replaced by a 'getAdapter()' method, and an AdapterManager is used to look up adapters in place of both adapt and the PEP 246 registry. So, it is much closer to PEP 246 than COM, in that COM all adaptation is managed by the object, and it cannot be externally adapted. (At least, the last I looked at COM many years ago it was the case; maybe that has changed now?)

So, I see transitivity as a nice thing to have IF it's something that gets explicitly asserted for a certain adapter -- if the adapter has to explicitly state to the system that it "isn't lossy" (maybe), or "isn't noisy" (perhaps more useful), or something like that... some amount of reassurance about the adapter that makes it fully safe to use in such a chain.

Maybe, although I think in our other thread we may be converging on definitions of lossy and noisy that are such we can agree that it's not really a problem. (I hope.)

Maybe it might suffice to let an adapter which IS 'lossy' (or, more likely, one that is 'noisy') state the fact.

I don't see a valid use case for implementing such a thing as an automatically-invoked adapter.

I'm always reluctant by instinct to default to convenient but risky behavior, trusting programmers to explicitly assert otherwise when needed; but in many ways this kind of design is a part of Python and works fine (with the BDFL's fine nose/instinct for making the right compromise between convenience and safety in each case, of course).

Proposal: let adaptation implemented via conform be nontransitive, and adaptation via adapt or the adapter registry be transitive.

This would mean that lossy or noisy adapters could be implemented as an implicitly-executed explicit conversion, but only directly on a particular concrete class and its subclasses, thereby further limiting the scope and impact of a lossy or noisy adapter.

Also, think about this: technically, if you implement lossy or noisy adaptation in conform, it isn't lossy or noisy, because you have to do it in the class -- which means that as the class' author, you have decreed it to have such semantics. However, if you are a third party, you will have to explicitly invoke the lossy or noisy adapter.

IOW, if you globally register an adapter (with either the interface or the global registry), you are guaranteeing that your adaptation is not lossy or noisy. Otherwise, you need to put it in conform or use it explicitly.

I'm still pondering the "don't adapt an adapter" suggestion, which seems a sound one, and yet also seems to be, intrinsically, what transitivity-by-chaining does. Note that QI does not suffer from this, because it lets you get the underlying object identity (IUnknown) from any interface adapter. Maybe, just maybe, we should also consider that -- a distinguished protocol bereft of any real substance but acting as a flag for "real unadapted object identity". Perhaps we could use 'object' for that, at least if the flow of logic in 'adapt' stays as in the current PEP 246 draft (i.e., conform is given a chance before isinstance triggers -- so, all adapters could conform to object by returning the underlying object being adapted, while other objects without such a feature in conform would end up with 'adapt(x, object) is x'). Or, if object in this role turns out to be confusing, IDentity (;-) or some other specially designed protocol.

It's a nice idea; the only problem I see is how far down it goes. Any adapter composition implies that adapters need to know whether to also call adapt(x,object) on their adaptee.



More information about the Python-Dev mailing list