[Python-3000] Sane transitive adaptation

Ian Bicking ianb at colorstudy.com
Thu Apr 6 17:50:46 CEST 2006


Nick Coghlan wrote:
> 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__.

RuleDispatch allows any function to be used in selecting an 
implementation, so implements() works as well as isinstance().  Well, 
truly making it work as well is some extra work, because RuleDispatch 
can determine the dominant implementation with isinstance(), but can't 
do so for an arbitrary function.

Since this aspect of RuleDispatch hasn't come up, it goes like:

   @foo.when("isinstance(a, Widget) and implements(b, IWibble)")
   def foo(a, b):
       ...

If general predicate dispatch doesn't go into py3k, that seems fine to 
me since it doesn't keep anyone from using RuleDispatch.  And having the 
concept in the core language can only benefit more sophisticated 
implementations.

But... interfaces.  Without predicate dispatch, how might it work?  I 
think you basically need two operations to do simple dispatch: a 
boolean, and something to detect when one condition is a subset of 
another.  For instance, if you have an implementation for A, and one for 
B, and B subclasses A, then you know with issubclass(B, A) that the B 
condition is a subset of A.  I guess you can get more complex about it 
too, with all the __mro__ discussion... but I haven't really kept track 
of those details.  Then you need a condition, isinstance(obj, B).

Since we don't know what interfaces look like, we don't know what these 
functions might be named.  But lets say issubinterface(IList, ISequence) 
will be true, and implements([], IList) is true.  So you just need to 
get your hands on these two functions... umm...

@generic
def is_predicate_subset(pred1, pred2):
     """Is pred1 a subset of pred2?

     If this is true, then an implementation specialized for pred1
     is dominant over an implementation specialized for pred2, because
     anything selecting for pred1 is statically identifiable as being
     more specific.
     """
     return False

@is_predicate_subset.when(type, type)
def predicate_classes(pred1, pred2):
     return issubclass(pred1, pred2)

@is_predicate_subset.when(interface, interface)
def predicate_interfaces(pred1, pred2):
     return issubinterface(pred1, pred2)

## though I guess you could also do:
# is_predicate_subset.when(type, type)(issubclass)

@generic
def matches_predicate(obj, pred):
     """Does obj satisfy the given predicate?
     """
     raise NotImplementedError

@matches_predicate.when(object, type):
def matches_class(obj, pred):
     return isinstance(obj, pred)

@matches_predicate.when(object, interface)
def matches_interface(obj, pred):
     return implements(obj, pred)


For performance reasons, it seems like you would want to resolve 
matches_predicate before the actual generic function is called.  So it 
might be:

@generic
def predicate_matcher(pred):
     """Return a function that tests if an object satisfies the predicate
     """
     raise NotImplementedError

@predicate_matcher.when(type)
def match_type(pred):
     return lambda obj: isinstance(obj, pred)

@predicate_matcher.when(interface)
def match_interface(pred):
     return lambda obj: implements(obj, pred)


So... in theory you could register strings, and thus implement 
RuleDispatches behavior (where those strings represent expressions). 
Or, ideally, py3k will have something better than strings to represent 
unevaluated expressions.  Though there's lots of optimizations in 
RuleDispatch that would be lost when abstracted in this way.

-- 
Ian Bicking  /  ianb at colorstudy.com  /  http://blog.ianbicking.org


More information about the Python-3000 mailing list