Method or function?

Alex Martelli aleaxit at yahoo.com
Thu Nov 2 10:16:01 CET 2000


"Rainer Deyke" <root at rainerdeyke.com> wrote in message
news:1E8M5.152239$g6.69207599 at news2.rdc2.tx.home.com...
    [snip]
> It bothers me that a function with two (or more) arguments which are
treated
> equally is implemented as a method of one of the arguments.  It's
asymetric.

I think the only _elegant_ way out of this asymmetry is the one indicated
by Dylan's multimethod-dispatch of generic functions, which treats all
arguments impartially.  "method-notation" in Dylan, foo.bar, is just
syntax sugar for a normal function call, bar(foo) [Dylan only allows this
specific sugar for 1-argument functions, but one might extend it to have
foo.bar(baz) mean bar(foo,baz), etc, I guess].

Despite the elegance, I suspect we won't see this in Python (even Py3k).

Multi-dispatch pays heavily for its elegance in terms of performance if
you have to resolve it dynamically every time -- determine the type/class
of each argument, sort all applicable methods for the actual types/classes,
complain if none or if there's ambiguity for 'best-match'.  I think Dylan
can only afford it because its typesystem is strongly oriented to get static
resolution of such issues in 'production' code -- if everything always
stayed as dynamically fluid as it does in Python, the cost (which one
pays on _every_ function call) might become crippling.

Single-dispatch of a method call, a la Python, affords far "snappier"
performance -- the conceptual model is simpler (aka, 'poorer':-): it's
"just like" a `normal' function call, except that you look-up the name
in a different way (depth-first left-first on the multi-inheritance DAG,
of the "singled-out" subject of the method-call).  Even the optimization
opportunities (though I don't think Python currently takes advantage
of them) are pretty simple -- the runtime might cache the lookup's results
per-class, cache to be invalidated only on presumably-rare events such
as changes in some class's __bases__ (occasional needs for even more
dynamic behavior being presumably already handled via __getattr__, which
does _not_ get called if the attribute is already in the dictionary...).

It may be possible to invent some mix of syntax sugar and semantics
conventions to gain some of the power of multi-dispatch (without its
full generality and elegance -- and without its full costs).  For
example, what if a call foo.bar(baz) _first_ tried the current strategy
to locate bar, but then, if that failed, before raising an exception,
switched to looking for bar as in a bar(foo,baz) call (or maybe some
suitably decorated version of bar).  This would let, say,
    'hello'.len()
return 5 without too much effort (one failed lookup among the string's
methods, then the fallback to len('hello') -- a bit slower than a
direct call for the latter, but probably not crippling).

foo.bar might then have to return the equivalent of a bound-method
even when bar is a function -- currying the first-argument foo; a
special case of closure.  But I get the impression (hope?-) that
_some_ kind of closures are eventually coming to Python anyway, so
that might not be a show-stopper...


Alex






More information about the Python-list mailing list