[Python-ideas] Democratic multiple dispatch doomed to fail (probably)
ntoronto at cs.byu.edu
Mon Dec 17 20:43:21 CET 2007
Stephen J. Turnbull wrote:
> Neil Toronto writes:
> > Python's __op__/__rop__ is a rather clever solution to the problem in
> > binary form. Do you know if there's a generalization of this? CLOS is
> > almost it, but Python lets the class containing __op__ or __rop__ own
> > the operation after it finds one that implements it.
> Sorry, no. I'm a professional economist (my early work was related to
> Gibbard-Satterthwaite, thus my interest here) and wannabe developer,
> who happens to have enjoyed the occasional guidance of Ken Arrow a
> couple decades ago.
> If you think there's something to the application
> of multiobjective optimization to this problem, I'd love to take a
> hack at it ... no classes to teach winter term, I've got some time to
> bone up on the multidispatch problem itself.
For finding the first matching function, you could say it's picking some
"best fit" from the Pareto optimal front. AFAIK all ways of doing that
suck somehow. :) After that, the problem is calling a next-method, which
requires imposing a total order on the entire space of fitting
functions. (This had better define the "best fit" as well.)
The big hurdles to multiple dispatch I see are:
1. It has to "make sense" somehow - maybe by satisfying some set of
2. It seems unlikely a majority will agree on the axioms. (Yay! Another
social choice problem!)
3. It has to be simple enough that it's easy to predict what a method or
next-method call does.
Because this seems nigh-impossible (particularly #3), I'm starting to
look elsewhere for solutions to "the problem". Speaking of which, it's
really easy to forget the two primary use-cases that motivate multiple
dispatch in the first place:
1. Interop with other types. For instance, I define a BarNumber type,
and I want fooing of BarNumber with anything else to return a new
BarNumber. Or perhaps I need some class I currently would have no
control over to treat my custom type specially or apply a transformation
before doing what it usually does. CLOS (Common Lisp Object System)
handles this. Python's __op__/__rop__ does for a finite set of binary
2. True dynamic overloading so we don't have to do type checks at the
heads of our functions. CLOS handles this.
#1 can always be done with clever delegation. For example, numpy's
arrays do it halfway by applying __array__ to a function argument before
operating on it. If they also applied, say, a type's fromarray
classmethod upon return it'd provide a full solution.
But as with numpy's arrays, users don't have enough control to make it
work exactly as they want if the only solution is delegation. The class
creator has to anticipate how his class will be used and risk
over-generalizing it. In the worst cases, you'd end up with a crazy
proliferation of types like FooLoggable and FooAppendable and methods
like __foo_loggable__ and __foo_appendable__...
The most general way to do #2 is the way it's done now. Sure it's
Turing-complete, but it'd be nice to be able to handle the common cases
PJE has a lot of further reading here:
More information about the Python-ideas