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;-).

...that was a bit too flippant -- I apologize. It DOES happen that interfaces keep changing, and other situations where "adapter-chain transitivity" is quite handy do, absolutely!, occur, too. Reflecting on Microsoft's QI (QueryInterface), based on a very strong injunction against changing interfaces and yet mandating transitivity, points that out -- that's prior art, too, and a LOT more of it than Eclipse can accumulate any time soon, considering how long COM has been at the heart of Microsoft's components strategy, how many millions of programmers have used or abused it. Still, QI's set of constraints, amounting to a full-fledged equivalence relationship among all the "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;-).


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 it might suffice to let an adapter which IS 'lossy' (or, more likely, one that is 'noisy') state the fact. 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).

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.

If we had this ability to "get at the underlying object" we could at least write clearer axioms about what transitivity must mean, as well as, help out with the "adapting an adapter" problems. E.g., imagine:

def f(x: IFoo, y: IFoo):
    if x is y: ...

that wouldn't work if adapt(x, IFoo) returns a separate adapter each time, which is the most likely situation (think, again, of str->file adaptation by StringIO wrapping); but recovering underlying identities by "adapt(x, object) is adapt(y, object)" would work.


I don't think that IUnknown or an equivalent, per se, can do *instead* of the need to have an adapter explicitly state it's non-noisy (or VV). Besides the need to check for object identity, which is pretty rare except when writing axioms, invariants or pre/post-conds;-), the IUnknown equivalent would perhaps be more of a conceptual/philosophical 'prop' than a practically useful feature -- while I see the ability to block unintended consequences of inheritance and transitivity (or even better, state explicitly when those consequences are wanted, even if that should be 90% of the time...) as practically very, VERY useful, even if "conceptually" or "philosophically" dubious.




Alex

_______________________________________________
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com

Reply via email to