Re: Overloading function calls (addendum)

Marc Wachowitz (mw@ipx2.rz.uni-mannheim.de)
5 Jun 1994 15:18:08 GMT

I wrote:
> The facility should of course be used for all methods, though it would only
> be applied automagically for the builtin operators. Other functions/methods
> would have to make an explicit request for it, like invoke_multi_method_for
> (args ...), though of course with a shorter name.

That is somewhat unclear - what I want to say is that multi-methods should
be usable for all functions/methods which want it. I'm not saying that all
methods should be converted to multi-methods. The facility should also not
be restricted to the two-arguments case, though for simplicity/efficiency,
a special version for this common case might exist (and be the only one at
the beginning).

Another clarification how a dispatching mechanism for multi-methods works,
at least conceptually (there are many optimization techniques, improving a
simple situation with pre-computation at registration-time, hashing, etc).

In the following, let's assume that all types are organized in an acyclic,
directed graph with a common root (the latter is helpful to simplify cases
where method selection doesn't discriminate on the type of an argument, or
as default method for all entities). The relation between types need not -
but could - be a superset of the subclassing rules (at least it should not
restrict subtyping to subclassing, separating conformance of behaviour and
inheritance of structure/code). Here the word "method" only refers to some
specific case of a multi-method, not to ordinary Python methods (if that's
ever going to be real, we'll have to find consistent and clear names). The
description probably sounds more complicated than the process really is.

First, find all applicable methods for the called name and argument types.
A method is applicable if it has the required name, and each expected type
of a parameter for this method includes the type of the corresponding call
argument. A type includes another type either if both types are identical,
or if the second type is directly or indirectly a subtype of the first one
(i.e. reflexive transitive closure).

Perform a topological sort on all applicable methods, yielding the partial
order according to the degree of specialization. A method is more special-
ized than another one, if at least one of its parameter types is a subtype
of the specialized type of the other method's parameter, and all the other
parameter types do at least include the other method's parameter type. Two
methods to which this ordering doesn't apply remain unordered. Thus we get
an ordered sequence of method-sets. If some identical method is registered
for several argument types, occurrences within the same set are merged.

If the set of most specific methods is empty, raise an exception ("message
not understood" in classical terms).

If there is more than one method in the most specific set, the call cannot
be resolved; raise an exception, too. One might think of some explicit way
to react to such ambiguities on a per-class and/or per-method basis, but I
wouldn't recommend a default handler for this case.

Otherwise, there is exactly one most specific method; apply that one.

There are many ways how one can allow a method to call a less specific one
(e.g. if it only wants to do something in addition to it, but the previous
one is still responsible for the main work). A relatively simple way would
be to provide a facility to select the applicable method for some explicit
set of types. That method must then only be called with arguments where it
is applicable, though it need not be the most specific method. Though it's
easy to think of other schemes based on the remaining sets computed above,
they will usually lead to ambiguities and/or unexpected behaviour.

------------------------------------------------------------------------------
* wonder everyday * nothing in particular * all is special *
Marc Wachowitz <mw@ipx2.rz.uni-mannheim.de>