One issue with generic functions and adaptation as currently being discussed (and something Tim mentioned a while back), it that it is very focused on dispatching based solely on obj.__class__.__mro__.
That's all well and good, but (to use an example of Tim's), suppose we have a couple of frameworks we're using, where those frameworks have each defined their own version of a "wibbly-wobbly" object: frmwrk_A.IWibble frmwrk_B.IWobble The methods and signatures included in these protocols may be the same, IWibble may be a subset of IWobble, or vice versa. Both frameworks have defined their interfaces (input and output) in terms of these protocols, rather than in terms of concrete types. As Tim pointed out, pure type-based dispatch would require that every type be registered with both protocols, even if the two protocols are identical. Doing that manually would be a serious pain if the frameworks were non-trivial. The first thought may be to allow adaptation between protocols - but the issue with that is that at adaptation time, we only have the object to work with, and won't know what protocols it claims to implement (this is where either __conform__ or a global registry comes in with PEP 246). Even if we did know, the cost of doing a search on all equivalent protocols on each call would add up. We don't really want to be finding ways to make functions calls even slower than usual ;) There is, however, an alternative, which would be to include a mechanism for telling a protocol about other equivalent protocols, and updating the registration mechanism to distribute any registration updates to the equivalent protocols. For example (not tested code!): def _update(self, updates, updated=None): # Update ourselves with a change # This update does NOT overwrite existing mappings changes = {} for signature, adapter in updates.iteritems(): if signature not in self.registry: changes[signature] = adapter self.registry.update(changes) self._distribute_update(changes, updated) def _distribute_update(self, updates, updated=None): # We don't want to get this update back if updated is None: updated = set() else: if self in updated: return updated.add(self) # Update any equivalent protocols # that have not yet been updated for protocol in self.subprotocols: if protocol not in updated: protocol._update(updates, updated) def _register_callable(self, signature, callable) # Explicit registration overrides existing mapping self.registry[signature] = adapter self._distribute_update({signature:callable}) def register_subprotocol(self, protocol): # Specified protocol is a subset of this one # so it can safely use this protocol's adapters protocol._update(self.registry) self.subprotocols.add(protocol) def register(self, *key) def helper(adapter): self.register(adapter, *key) return adapter return helper Using such a mechanism, the interface mismatch above could be addressed in one of the following ways: Suppose framework A specifies fewer methods than framework B, but those methods match. Then you can write: frmwrk_B.IWobble.register_subprotocol(frmwrk_A.IWibble) # Now registration for framework B also registers you for the narrower # interface in framework A You can turn that around, if A is the one that is more prescriptive: frmwrk_A.IWibble.register_subprotocol(frmwrk_B.IWobble) # Now it is registration for framework A that registers you for the narrower # interface in framework B And finally, if the two interfaces are identical: frmwrk_A.IWibble.register_subprotocol(frmwrk_B.IWobble) frmwrk_B.IWobble.register_subprotocol(frmwrk_A.IWibble) # Now registration for either framework registers you for both Now, suppose, however, that mapping from A to B required a slight tweak to A's interface - one of the method signatures didn't line up right (e.g. one of A's methods has the wrong name). This can be handled with an explicit protocol adapter, which would provide a modified update method like so: class ProtocolAdapter(object): def __init__(self, src, target): self.target = target src.register_subprotocol(self) def _update(self, updates, updated=None): if self.target in updated: return wrapped_updates = {} for signature, adapter in updates.iteritems(): def wrapped_adapter(*args, **kwds): return self.adapt(adapter(*args, **kwds)) wrapped_updates[signature] = wrapped_adapter self.target._update(wrapped_updates, updated) def __call__(self, adapter): self.adapt = adapter class AdaptWibbletoWobble(object): def __init__(self, obj): self.obj = obj def __iter__(x, y, z): return self.obj.method2(x, y, z) ProtocolAdapter(frmwork_A.IWibble, frmwrk_B.IWobble)(AdaptWibbleToWobble) The equivalent of the above for generic functions is the case where "generic function A" does a very similar thing to "generic function B", and you want to be able to do a wholesale delegation from A to B of everything that B handles more specifically than A. Aside from the fact that I think any such transitivity mechanism should be observer based, and that the updates should happen at registration time rather than lookup time, I'm not really wedded to any of the implementation details above. But I figured it was something worth throwing out there :) Cheers, Nick. -- Nick Coghlan | [EMAIL PROTECTED] | Brisbane, Australia --------------------------------------------------------------- http://www.boredomandlaziness.org _______________________________________________ Python-3000 mailing list Python-3000@python.org http://mail.python.org/mailman/listinfo/python-3000 Unsubscribe: http://mail.python.org/mailman/options/python-3000/archive%40mail-archive.com