[Python-3000] Generic functions

Ian Bicking ianb at colorstudy.com
Mon Apr 3 19:25:29 CEST 2006


As an alternative to adaptation, I'd like to propose generic functions. 
  I think they play much the same role, except they are much simpler to 
use and think about.

Though RuleDispatch offers considerably more features through predicate 
dispatch, it would probably be best to just consider type based 
dispatch, as that's more equivalent to adaptation.

So, the copy_reg module is one example where adaptation has been 
proposed.  The idea is that you adapt an object to a pickleable object, 
or something along those lines.  It's a little vague, because while you 
typically adapt an instance coming in, you "adapt" a string to a new 
instance on the way out.  Or, I dunno, it's not clear to me.  In fact, 
though that's the typical example, I'm going to bail on that because the 
pickling protocol is an aside to this and too complex for me to digest 
right now.  pprint is a lot easier, and conveniently is much nicer with 
generic functions than adaptation ;)

Anyway, pprint could work like:

class PrettyPrinter:
     @generic
     def pformat(self, object):
         """Return the pretty string representation of object"""
         return repr(object)
     # pformat is now "more" than just a function, it's a callable
     # object that does type-based dispatch using an internal registery
     # of implementations, with the implementation above as the fallback.

# It also now can be used as a decorator that registers implementations:
@PrettyPrinter.pformat.when(object=list)
def pformat_list(self, object):
     s = '['
     for item in object:
         s += (' '*self.indent) + self.pformat(item) + ',\n'
     return s + (' '*self.indent) + ']'


Some things to note:

* There's no interface created here.  There's no hidden interface 
lurking in the background either.

* It requires cooperation from the original function (pformat -- I'm 
using "function" and "method" interchangably).  It does not require any 
cooperation from classes like list, similar to adaptation and dissimilar 
to magic methods.  Adaptation also requires cooperation from the caller, 
as the adaptation would be applied inside pformat.

* The function is mostly self-describing.  If it has certain return 
values, then you state what those values are in documentation; there's 
no need to be formal about it.  In contrast you have to come up with a 
whole collection of interfaces to start using adaptation.

* The function is the hub of all the registration, which seems very 
natural, since you are extending the function.

* Like adaptation, you must import a module that defines extra 
specializations of the generic function before those are active (just 
like you have to import adapter declarations).  This strikes me as a 
significant problem.  I assume ZCML addresses this, but I also assume 
that's not a reasonable solution for core Python.

* Magic methods do *not* have this import problem, because once you have 
an object you have all its methods, including magic methods.

RuleDispatch has a bunch more features than just simple type-based 
generic functions.  But I think that type-based generic functions would 
be an easier or more comfortable place to start, and wouldn't preclude a 
more featureful implementation later.

Type-based generic functions and adaptation are more-or-less equivalent. 
  That is, you can express one in terms of the other, at least 
functionally if not syntactically.  If you really wanted adaptation, 
then the interface becomes a things-obeying-this-interface factory -- 
i.e., a generic function.  Generic functions are similer to 
multi-adaptaters, where you adapt a tuple of objects, similar to the 
tuple of arguments to a function.  This is technically like generic 
functions, but syntactically rather awkward.

[Predicate-based dispatching goes considerably further, allowing real 
duck typing, e.g., you could implement a pformat method for everything 
that has an "__iter__" method and no "next" method (i.e., all iterables, 
but not iterators which could lead to unintentionally consuming the 
iterator).]

Anyway, I think generic functions are very compatible with Python syntax 
and style, and Python's greater emphasis on what an object or function 
can *do*, as opposed to what an object *is*, as well as the use of 
functions instead of methods for many operations.  People sometimes see 
the use of functions instead of methods in Python as a weakness; I think 
generic functions turns that into a real strength.

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


More information about the Python-3000 mailing list