[Python-Dev] Re: Multimethods (quelle horreur?)

David Abrahams David Abrahams" <dave@boost-consulting.com
Sat, 17 Aug 2002 17:57:39 -0400


From: "Samuele Pedroni" <pedroni@inf.ethz.ch>

> the question was whether
> adding a method to a gf
> is always the moral equivalent of
>
>  lib.py:
>
> class A:
>    def meth(self,...): ...
>
>  class B(A): ...
>
>  class C(B):
>    def meth(self,...): ...
>
>  abusive_user.py:
>
>  from lib import A
>
>  def foo(...): ...
>
>  A.meth=foo

And the answer is, "clearly not always".

>  > Well, I still don't get it. I clearly don't know what "fiddling"
means,
> > since any added signature can change the behavior of the multimethod. I
> > think I would be inclined to forbid your first case, where you're
adding a
> > multimethod implementation whose signature exactly matches another one
> > > that's already in the multimethod.
>
>  The point is whether the behavior is changed in an undetected way
> with respect to sets of arguments for which some matching signature/
> method is already defined. So my conditions
>
>  add(M,(h,T3))  with T3==T2 (*) or T1<T3<T2.
>  (assuming that T3==T2 triggers substitution) .
>
>  [T3==T2 case corresponds to the above
>
>  A.meth = foo

I hope you are covering this case just for generality's sake. It's easy
enough to forbid.

>  T1<T3<T2 correspond to the single dispatch case:
>
>  from lib import B
>
>  B.meth=... ]

I don't understand why you're using such a complicated condition; you can
change the behavior "in an undetected way WRT sets of arguments for which
some matching signature/method is already defined" simply by adding a
signature T4 s.t. T4 < X for some X in the signatures of the multimethod.

>  If T3 is < or uncomparable with all the signatures
>  already in M:
>  - you are doing the moral equivalent of overriding
>  in the single dispatch case

Sort of. You might not be the same person that supplies the types in T3.

>  - or you are defining the gf for some unrelated
>  class hierarchies

Yep.

>  - or some case that was unambiguous
>  will become ambiguous and the outcome
>  will depend on the rules you choose to
>  deal with ambiguity (which is a general
>  problem with multidispatching).

Yep.

> > > [Btw up to (*), missing a module or calling
> > > a generic function before all modules are loaded,
> > > load order does not count.]
> > The above sentence is completely incomprehensible to me.
>
>  Dispatching outcomes are invariant wrt
> the order by which you add gf-methods to a gf.

It depends on your dispatching rules, of course. However, I'd like to pick
order-independent rules.

> > > See my posted code for the idea of redispatching
> > > on forced types, which seems to me reasonably Pythonic
> > > and allows OTOH to choose a very strict approach
> > > in face of ambiguity because there's anyway a user
> > > controllable escape.
> >
>  > Could you please explain your scheme in plain English?
> > What is a "forced type"?
>
>  the idea is to allow optionally to specify together
> with an argument a supertype of the argument and to have
> the dispatching mechanism use the supertype instead
> of the type of the argument for dispatching:
>
> gf(a,b,_redispatch=(None,SuperTypeOf_b))
>
> the dispatch mechanism will consider the
> tuple (type(a),SuperTypeOf_b) instead
>  of (type(a),type(b)) for dispatching.

More "sugarily:"

    gf(a, dispatch_as(b, SuperTypeOf_b))

Interesting. Not sure how I feel about this.

>  This is the moral equivalent of
>  single dispatching:
>
>  SuperTypeOf_b.meth(b)
>
>  or super(SuperTypeOf_b).meth(b)

Hmm. OK, I see the analogy. I hardly ever have to do that even in the
single case, but I get what you're up to.

> > > My opinion: left-to-right and refuse ambiguity
> > > are depending on the generic function both
> > > reasonable approaches.
>
>  > I assume that "left-to-right" is some kind of precedence ordering for
> > ambiguous multimethod implementations. Can you give an example where
that
> > would be appropriate?
>
> is the default used by CLOS, that simply means that
> signature (type tuples) are compared using the lexico-order,
>
> given
>    class A: pass
>    class B(A): pass
>
>  then (B,A)<(A,B)
>
>  you get the same effect as multidispatching
>  simulated through chained single dispatching.

That seems a bit arbitrary, but I guess there are other precedents in
Python for an arbitrary ordering (e.g. ordering on type names for
heterogeneous object comparison).

> > > The proposed notation or whatever should be at most
> > >  just syntax sugar:
> > >
> > > (a,b,c).f(d) === f(a,b,c,d) in general.
> >
> > All notations (except really ugly ones) are syntax sugar. What point
are
> > you trying to make?
>
> what I was trying to convoy is that it would
> be bad to have multimethod invocation be a special
> operation different from the usual function invocation
> (which currently is at work also for method invocation).

Agreed.


>  > It now looks like you were trying to say in 3. that multimethods
should be
> > invokable and definable in the same way as single-methods.
>
>  no, I was saying that at most
>
>  (a,).gf(b) should just be equivalent to gf(a,b) (*)
>
>  but  plain a.meth() should still mean what it means today.
>
>  But honestly I find (*) unnecessary and ugly.
> I'm not even sure one can really disambiguate
> such syntax:
>
>  (a,b).__contains__(2)
>  (a,).__contains__(2)
>
>  are valid Python.
>
>  >Well, I'm not
> > sure that this elegant idea from Dylan is essential or even
neccessarily
> > good for Python. It's cute, but what does it really buy us?
>
> nothing. My point is that once we have multimethods,
> one has the choice, one can
> define classes without normal methods and just use multimethods instead,

There's the (minor?) issue of access to "private" members whose names begin
with two underscores.

>  I was not advocating that C().meth() should be equivalent
>  to meth(C()) in every respect and that the method definitions
>  inside the class definitions should triggers gf-method
>  definitions. It would be a disruptive change for Python.
>
> So we agree.

Good!

> Multidispatching functions should be an extension of the notion
> of function in Python not of class methods. OTOH
> class methods are defined by defining functions
> inside class namespaces, so it should be possible
> to get class methods also from gfs defined
> in a class namespace.

Yes, I strongly agree.

-Dave