[Python-Dev] Concrete proposals for PEP 246 (original) (raw)
Phillip J. Eby pje at telecommunity.com
Tue Jan 11 21:53:08 CET 2005
- Previous message: [Python-Dev] Re: PEP 246: LiskovViolation as a name
- Next message: [Python-Dev] copy confusion
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
To save everyone from having to wade through the lengthy discussions between Alex and I, and to avoid putting all the summarization burden on Alex, I thought I would start a new thread listing my concrete proposals for PEP 246 changes, and summarizing my understanding of the current agreements and disagreements we have. (Alex, please correct me if I have misrepresented your position in any respects.)
First, I propose to allow explicitly-declared Liskov violations, but using a different mechanism than the one Alex has proposed. Specifically, I wish to remove all type or isinstance checking from the 'adapt()' function. But, the 'object' type should have a default conform that is equivalent to:
class object:
def __conform__(self,protocol):
if isinstance(protocol,ClassTypes) and isinstance(self,protocol):
return self
return None
and is inherited by all object types, including types defined by extension modules, unless overridden.
This approach provides solid backward compatibility with the previous version of PEP 246, as long as any user-implemented conform methods call their superclass conform. But, it also allows Liskov-violating classes to simply return 'None' to indicate their refusal to conform to a base class interface, instead of having to raise a special exception. I think that, all else being equal, this is a simpler implementation approach to providing the feature, which Alex and others have convinced me is a valid (if personally somewhat distasteful) use case.
Second: Alex has agreed to drop the "cast" terminology, since its meanings in various languages are too diverse to be illuminating. We have instead begun creeping towards agreement on some concepts such as "lossy" or "noisy" conversions. I think we can also drop my original "lossy" term, because "noisy" can also imply information loss and better explains the real issue anyway.
A "noisy" conversion, then, is one where the conversion "makes up" information that was not implicitly present in the original, or drops information that alters the semantics of the information that is retained. (This phrasing gets around Alex's LotsOfInfo example/objection, while still covering loss of numeric precision; mere narrowing and renaming of attributes/methods does not alter the semantics of the retained information.)
Adaptation is not recommended as a mechanism for noisy conversions, because implicit changes to semantics are a bad idea. Note that this is actually independent of any transitivity issues -- implicit noisy conversion is just a bad idea to start with, which is why 'someList[1.2]' raises a TypeError rather than implicitly converting 1.2 to an integer! If you mean to drop the .2, you should say so, by explicitly converting to an integer.
(However, it might be acceptable to implicitly convert 1.0 to an integer; I don't currently have a strong opinion either way on that issue, other than to note that the conversion is not "noisy" in that case.)
Anyway, I think that the current level of consensus between Alex and myself on the above is now such that his comparison to casting could now be replaced by some discussion of noisy vs. non-noisy (faithful? high-fidelity?) conversion, and the fact that adaptation is suitable only for the latter, supplemented by some examples of noisy conversion use cases and how to transform them into non-noisy constructs. The string->file vs. string->file_factory example is a particularly good one, I think, because it shows how to address a common, practical issue.
Third: (Proposed) The PEP should explicitly support classic classes, or else there is no way to adapt exception instances. (Yes, I have actually done this; peak.web adapts exception instances to obtain appropriate handlers, for example.)
Fourth: The principal issue from the original discussion that remains open at this time is determining specific policies or recommendations for addressing various forms of transitivity, which we have delved into a little bit. (By "open" I jut mean that there is no proposal for this issue currently on the table, not to imply that my proposals are not also "open" in the sense of awaiting consensus.)
Anyway, the kinds of transitivity we're discussing are:
interface inheritance transitivity (i.e. adapt(x,IBase) if adapt(x,IDerived) and IDerived inherits from IBase)
adapter composition transitivity (i.e. adapt(x,ISome) if adapt(x,IOther) and there is a general-purpose ISome->IOther adapter available.
These are mostly issues for the design of an interface system (implementing adapt) and for the design of a global adapter registry. I don't think it's practical to implement either kind of transitivity on the conform side, at least not for hand-written conform methods.
To summarize current PEP 246 implementations' choices on this issue, Zope implements type 1 transitivity, but not type 2; PyProtocols implements both. Both Zope and PyProtocols allow for individual objects to assert compliance with an interface that their class does not claim compliance with, and to use this assertion as a basis for adaptation.
In the case of PyProtocols, this is handled by adding a per-instance conform, but Zope has a separate concept of declaring what interfaces an instance "provides", distinct from what it is "adaptable to". PyProtocols in contrast considers "provides" to be the same as "adaptable to with no adapter", i.e. a trivial special case of adaptation rather than a distinct concept.
I have also asserted that in practice I have encountered more problems with type 1 transitivity than with type 2, because of the strong temptation to derive an interface to avoid duplicating methods. In other words, inappropriate use of interface inheritance produces roughly the same effect as introducing a noisy adapter into a type 2 adapter mesh, but IMO it's much easier to do innocently and accidentally, as it doesn't even require that you write an adapter!
- Previous message: [Python-Dev] Re: PEP 246: LiskovViolation as a name
- Next message: [Python-Dev] copy confusion
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]