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

David Abrahams dave@boost-consulting.com
Sat, 17 Aug 2002 16:48:39 -0400

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

> [f,g,... are functions ;  T1,T2,T3 are type tuples a.k.a multi-method
> signatures, T1<T2 means the corresponding elements
> are "subtypes" ]
> 1. if I see things correctly, you are really fiddling with a multi-method
> M = { (f,T1), (g,T2), ... }from a library
> only if you do e.g.
> add(M,(h,T3))  with T3==T2 (*) or T1<T3<T2.
> [Things one typically does not do without thinking
> twice]

Let's see if I can understand this in plain English. Hmm, maybe I shouldn't
try. You've mixed hard-core formalism and lax informalism here. What does
"fiddling" mean?

Against my better judgement, I'll try anyway. I think you're saying:

    "The only way you're changing the behavior of a multimethod [what I
think you mean by "fiddling"] is if

    1. the type signature of the implementation you add duplicates one
       that's already been added,


    2. it can match all the same types as another one (g,T2), and for
       which there's another implementation (f,T1) which can match all
       the same types as the new implementation. In other words, it
       falls between two other signatures in specificity.

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.

> [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.

> If T3 is < or incomparable with all the signatures in M,
> is not fiddling.

Okay, you're homing in on a formal definition of "fiddling" here, but I
still don't know what its significance is.

> Incomparable can mean dispatch ambiguities
> but that's a different issue.

I guess so, by definition (yours <wink>).

> import lib
> class Sub(lib.Class1):
>   ...
> class New:
>  ...
> addmethod lib.gf(x: Sub,y: Sub):
>   ...
> addmethod lib.meth(arg: Sub):
>   ...
> is no different than defining new classes and subclassing and
> overriding methods. Also the kind of resulting program
> logic scattering is not that different under normal usage.

I agree.

> 2. Dispatch ambiguities: the more predictable the rule
> the better,


> the best-fit of multimethod does not match
> such a criterion, see my previous postings.
> Rules for CLOS:
> http://www.lispworks.com/reference/HyperSpec/Body/07_ffab.htm
> (NB things are eminently configurable in CLOS)
> Rules for Dylan:
> http://www.gwydiondylan.org/drm/drm_50.htm

I'm away from the internet at the moment, so I can't look...

> The class predecende list is the same notion
> as Python 2.2 mro.
> 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"?

> 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?

> 3. IMO documentation, doc strings, and introspection
> should be enough to tell generic functions apart.


> 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?

> Generic functions should be first-class object
> that can be passed around and used everywhere
> functions can be. Already today f(a,b)
> can invoke a function, a callable instance
> (maybe implemeting some multimethod logic given that
> one can write multimethod support in pure Python),
> a bound method ...

of course.

> 4.  AFAIK multimethods where invented in environments
> with code developed and defined in memory incrementally
> and libraries loaded, that means that through introspection
> one could list the methods of a generic functions and jump
> to the various definitions points, and warnings could
> be issued for redefinitions and such (see 1.).
> For more static approaches to introspection
> syntactic sugar would be probably useful:
> defgeneric addmethod vs. f.add(...)

I don't understand why you keep applying the term "generic" here. Ordinary
python functions are already as generic as you can get. Multimethod
implementations are less-so.

> Of course a Python impl could also
> optionally emit warnings etc, this requiring
> the good practice to load library code before
> user code, and being maximally useful in
> an incremental environment.

of course.

> 5. It is true that once you have multimethods you have
> the choice:
> class C:
>   def meth(...): ...
> vs.
> class C: ...
> defgeneric meth
> addmethod meth(obj: C): ...

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. 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?

> 6. It is true that generic functions kind of add
> a new degree of freedom to the modularity problem
> space.


> PS: this is my input together with what I have already
> posted, if something is unclear please ask.


           David Abrahams * Boost Consulting
dave@boost-consulting.com * http://www.boost-consulting.com