[Python-3000] Adaptation [was:Re: Iterators for dict keys, values, and items == annoying :)]

Alex Martelli aleaxit at gmail.com
Mon Apr 3 03:27:31 CEST 2006


On Apr 2, 2006, at 4:39 PM, Walter Dörwald wrote:
    ...
> Why not make the registry identical to the protocol? The protocol  
> is just a convention anyway:

Yes, a 1<->1 relationship between registries and protocols makes the  
'registryof' function I was talking about simpler, at least when it's  
implemented as identity.  What's the advantage of this approach,  
compared to allowing protocols to be arbitrary hashable attributes?   
The disadvantage, essentially, is that one gives up the degrees of  
freedom implied by protocols being "just a convention": a protocol  
must now (say) be a callable object exposing a register method (with  
given signature and semantics).

I guess adopting that kind of constraint is OK, as long as the  
constraint doesn't in any way interfere with whatever else Guido  
wants to do with protocols and thus give him a reason to reject  
adaptation. I guess I can see some potential for minor optimization:  
a protocol that's written with some existing types already in mind  
might use a subclass of your 'Adaptor', such as:

class FastTracker(Adaptor):
     def __init__(self, types):
         self.fastrack = set(types)
         Adaptor.__init__(self)
     def _anyof(self, types):
          for t in types:
              if t in self.fastrack: return True
          return False
     def register(self, adaptor, types):
         if self._anyof(types):
             # need better diagnostics here, of course
             raise RegistrationError, "Cannot override identity- 
adaptation"
         return Adaptor.register(self, adaptor, types)
     def __call__(self, obj, *a, **k):
         if self._anyof(type(obj).__mro__):
             if a or k:
                 raise ...some kind of diagnostics about a/k not  
allowed here...
             return obj
         return Adaptor.__call__(self, obj, *a, **k)

I'm not sure the advantage is big enough to warrant all the  
machinery, but I can't assert it isn't, either.

>> Isn't it just wonderful, how the foes of adaptation switch horses  
>> on  you?  First they request a simple-as-dirt, bare-bones  
>> "example  system" -- then as soon as you provide one they come  
>> back at you with  all sort of "cruft" to be piled on top.
>
> I think you might be misinterpreting reactions. If the initial  
> reaction was "I don't understand it. Nobody needs this." (at least  
> that was my reaction), you're "strawman proposal" has put us past  
> this. (At least you got two "I finally got it, this seems useful"  
> from me and Brett.)

You're quite likely right, and I apologize for the misinterpretation;  
I guess I may have misread Brett's desire to hammer adaptation's very  
reason for being down to miniscule smithereens by having the  
existence of some set of methods "imply" identity-adaptation, as a  
subconscious desire to defend against the whole idea of adaptation in  
the guise of sort-of-accepting it, for example.

> So now lets answer the questions: How do we implement adaption of  
> subtypes? What is a protocol? How can we make registration as  
> painless as possible?  etc.

Looks like the "loop on __mro__" idea is sort of standard for the  
first of these questions.  As for "what is a protocol", I'd rather  
minimize the machinery that goes with it, but I guess that's some  
kind of holdover from the "strawman proposal"; if we're making  
protocols into classes anyway, I'd like to provide them with a  
check_compliance method, taking an object and an integer that  
represents the amount of effort to be spent in the checking -- 0  
meaning 'just check existence of methods', 1 meaning 'check methods'  
signature compatibility too', 2 meaning 'check some semantics at a  
level suitable for fast unit-tests', and so on up.  The author of a  
protocol doesn't have to do all that much (levels 0 and 1 can be  
provided by helper functions/methods relying on the inspect module),  
but he or she CAN make the protocol into "an executable  
specification" at whatever level he or she desires. Other error  
checking (e.g., against registration of multiple adapters for one  
protocol/type pair) as well as some modest optimization (mostly of  
adaptation, the frequent case) may also be warranted.

I'm not sure what's painful at all about registration -- at worst  
it's one call per type/protocol pair, some refactoring such as yours  
making it even slighter by allowing the registration of several pairs  
in one call.  Do you mean that people are so keen on declarative  
styles that they'd crave, e.g., some special machinery such as

class Duck(object):
     __implements__ = walk, talk, quack
     ...

(with the metaclass doing the registration calls, with identity  
adaptation, under the cover for each protocol in __implements__),  
prizing it highly over explicit:

class Duck(object):
    ...
adapt.register(Duck, (walk, talk, quack), identity)

or the loop or classdecorator version of the latter?  Ah well, maybe,  
but most of the discussion sort of smacks to me of AGNI and/or  
premature optimization.

Could we agree that simplicity and even minimalism (with a side order  
of future extensibility if/when warranted) are high on the list of  
desired properties for a protocol adaptation mechanism?  After all,  
the more machinery, cruft, and black magic we pile on top of  
adaptation's simple core ideas, the more likely the whole caboodle is  
to provoke allergic reactions in anybody who's not being a part of  
this discussion...


Alex



More information about the Python-3000 mailing list