|
Re: Providing standard protocols
|
Posted: Jan 10, 2005 2:06 PM
|
|
> > > 1. To implement a class that provides the complete > mapping > > > protocol, i only have to write 3-4 basic methods. > When i > > > derive my class from a standard Mapping interface, > the > > > rest of the methods get filled in for me. > > > > The time machine seems to have solved this one for you: > > see UserDict.DictMixin which appears to be exactly what > > you ask. > > That's not quite what i'm talking about. I'm aware that > this functionality can be brought in with a mix-in class; > what i'm suggesting is that this be part of the > standard interface. For example, if a Mapping
So, basically, you want to make the interface into an abstract class instead, offering "template methods" which call back to a few lower-level "hook" methods -- just like, say, DictMixin does today, except that DictMixin is more flexible. If you have, say, a readonly mapping, you can still use DictMixin to give you many readonly "richer" methods -- if somebody tries to call a read-write "richer" method, it will fail one or two levels down when DictMixin's implementation calls to the __setitem__ (say) which you don't implement (or in which you raise an exception). You can also use DictMixin without claiming to conform to the dictionary protocol. For example, consider shelve -- can it really be said to conform to the dictionary protocol, when it can only accept strings, not arbitrary hashables, as keys? For DictMixin it doesn't matter: thanks to signature-based polymorphism, it can still work just fine to provide higher-level methods by Template DP even under such restrictions.
So, even if Mapping (carefully and painstakingly segregated -- at least -- into readonly and readwrite variants), RandomAddressableSequence (ditto), etc etc, all came with the relevant mixins bundled in, I would still want to have those mixins available in the standard library separately from the interfaces, because pragmatically speaking they're still very useful.
> interface is introduced, i'd like it to do this as part of > its definition so we can be confident that all > implementors have it. More generally, i'd like this to be > part of the way interfaces typically work, not just to > have a couple of one-off mix-ins in the library for > mappings and sequences.
Maybe a special mechanism is warranted to let a "pure" interface (where the contents of methods should really represent checks, pre/post conditions, and the like, rather than default implementations) indicate one (or more, see below) "supporting mixins" to be added to classes claiming to implement the interface. I believe that the existence of a good set of pure interfaces and supporting mixins for the typical protocols found in the standard library is more likely to be the determinant of how interfaces will typically be designed, rather than any such "bundling" mechanism, but that's just a guess, and I could be wrong. In any case, the ability to use the mixins separately from the interfaces would remain important, as above indicated.
> Both of the things i suggested in the original comment, > which i guess you could call API-widening-by-interface and > API-widening-by-adaptation, are aimed at improving > flexibility and resilience by giving the client of an > object more assurance that all the methods will be present > and have the expected semantics. > > Does that make sense?
I think it does, and indeed it prompts me to raise the ante by going back to my original idea, which was to have in Python a closer equivalent to Haskell's typeclasses, rather than to pure interfaces or abstract classes.
A typeclass is more powerful and flexible than an abstract class because it need not specify which methods are abstract, aka lower-level, in other terms exactly which methods need to be supplied by an object to claim compliance (and to possibly get the Template DP implementations of the others in terms of the ones it does supply). As a toy example:
typeclass IWriteable: def write(self, onething): self.writelines([onething]) def writelines(self, manythings): for onething in manythings: self.write(onething)
this looks like write and writelines are mutually recursive, but that's not what it means: it means that a class claiming to implement IWriteable must supply either write or writelines (it may also supply both, for performance reasons). If it only supplies EITHER one, the other is supplied Template DP - like from the typeclass.
If we had typeclass, then we'd need the metaclass to determine the dependencies at typeclass level, then when a concrete class is being built, find out which methods the concrete class supplies and pick the others appropriately (or give an error if the concrete class just doesn't supply a sufficient baseset of concrete methods).
An almost equivalent alternative might be to have an interface be able to specify several mixins depending on the sets of concrete methods supplied by the concrete class which is claiming to implement the interface; this would offer less automatism, more explicitness and control, perhaps more readability, and also accomplish the key purpose of ensuring the mixins are also available for separate use.
I don't really know which of the alternatives on a growing scale of depth and elegance -- provide no special mechanism, provide a special mechanism that can only deal with one mixin, provide one that can deal with several, implement full-fledged typeclasses... -- would give the best bang for the buck. Maybe we should widen this discussion by moving to python-dev...
Alex
|
|