[Python-3000] Adaptation: T->P vs P->P
Nick Coghlan
ncoghlan at gmail.com
Mon Apr 3 15:36:53 CEST 2006
FWIW, I went back and read PEP 246. That PEP goes to great lengths to permit
arbitrary objects to be used as protocols, with the process of checking for
compliance being a bit of a tap dance back and forth between the object and
the protocol. Great effort was also put into registering adaptability without
mutating any attributes of the protocol object.
The current discussion has persuaded me that it's better to require that
protocols be special types, themselves providing a particular interface for
adaptation purposes, and having ultimate authority over what it means to be
compliant to themselves (thus allowing, e.g. an IIndex protocol to consider
anything with a zero-argument __index__ method to be compliant - the act of
defining a special interpreter method is a fair indication that the class
considers itself compliant with that protocol).
The fact that PyProtocols, Twisted and Zope all use special objects to define
their interfaces is also a factor in forming that opinion. The PyProtocols
docs mention the possibility of using arbitrary objects, and then points out
that doing so means you miss out on most of the benefits of PyProtocols. Yet
the usability of the entire API suffers for the sake of that genericity.
So I'd be inclined to make the interface for a protocol reasonably fat by
including things like adapter and implementation registration as methods.
With such an approach (and class decorators), it would be straightforward to
write:
class Duck(object):
"""The duck class"""
@class implements(IDuckWalk, IDuckQuack, IDuckLook)
# ...
where implements works something like:
def implements(*interfaces):
def register_class(cls):
for interface in interfaces:
interface.register_type(cls)
return register_class
The protocol itself can preregister known good classes in its class definition.
Third parties that know a particular class implements a particular protocol
can simply register that class directly.
Tim Hochberg wrote:
> In this thread, Alex has been advocating adaption where types are
> adapted to protocols: T->P adaption for short. By contrast, my two
> sample implementations have involved Protocol->Protocol adaption where
> objects that implement a certain protocol are adapted to another
> protocol: P->P adaption for short. I suppose this also could be
> considered T->P->P adaptation since you look up the protocol from the
> type, but let's stick with the terminology P->P adaption.
>
> Alex has been advocating for adaption for quite a while (I imagine it
> seems like forever to him), so I give his views here great weight.
> Still, something about T->P adaption has been bothering me, but until
> now I haven't been able to put my finger on it beyond a vague sense that
> pressing concrete types into service in this way is asking for trouble.
>
> Here's the problem I have with T->P adaption: it increases coupling
> between the various clients of the adaption process. Lets talk about
> these clients, I believe Alex said there were four:
>
> 1. The author of the type: T
> 2. The writer of the adapter: A
> 3. The person defining the destination protocol: P
> 3. The user of the whole shebang: U
>
> Now under a T->P regime, T needs to search out all relevant adapters and
> register them for their type. Similarly when adding a new adapter, A
> needs to search out all relevant classes and register the new adapter
> for them. Thus A and T become highly coupled.
This misses the whole point of dynamic adaptation. T and P might define a few
convenience adaptations (e.g. to or from standard library interfaces), but A
and U will usually be the same person. Suppose framework X produces a Wibble,
and framework Y expects an IWobble in various places. The integrator (U) needs
to plug them together. If Wibble provides the right interface, U can simply write:
IWobble.register_type(Wibble)
Or, more commonly, U may need to write an adapter:
class WibbleAsIWobble(object):
def __init__(self, the_wibble):
self.the_wibble = the_wibble
# delegate the IWobble API to the Wibble instance
IWobble.register_type_adapter(Wibble, WibbleAsIWobble)
Either way, after either conformance or the adapter have been registered, a
Wibble can be used seamlessly anywhere an IWobble was expected.
On a completely different note, now that I've researched things a bit further,
I think PyProtocols really has done a good job thrashing out the issues
associated with adaptation. The *only* thing I really disagree with in
PyProtocols is that I think the API usability has suffered due to the fact
that state is spread out amongst not only protocol objects, but is also
sometimes stored on the types being adapted.
This leads to all sorts of "well sometimes you can do this, but sometimes you
can't, depending on what you're adapting, so maybe you shouldn't rely on it"
caveats. If the protocol API itself was richer (mandating that protocol
objects support dynamic registration), then the API could be simplified
significantly (as most operations would simply become method invocations on
the target protocol, and the source objects could be whatever you wanted,
since the state would all be on the protocol objects).
Regards,
Nick.
--
Nick Coghlan | ncoghlan at gmail.com | Brisbane, Australia
---------------------------------------------------------------
http://www.boredomandlaziness.org
More information about the Python-3000
mailing list