Add multiple dispatch as external dependency

251 views
Skip to first unread message

Matthew Rocklin

unread,
Apr 1, 2014, 10:10:16 PM4/1/14
to sy...@googlegroups.com
Hi Everyone, 

i'd like to add the multipledispatch project as an external dependency to SymPy.

As a reminder, SymPy currently doesn't depend on any external libraries, so this particular move also represents a policy shift.  This potential policy shift follows substantial discussion, notably at https://github.com/sympy/sympy/issues/7339 .  There are good reasons for and against external dependencies (I recommend checking out the header of the mentioned issue).  
I'm now specifically proposing that we depend on multipledispatch which might have other more specific benefits and drawbacks.  Even if someone is for dependencies they might not be for this particular dependency.  

Any thoughts?  Now would be a good time to voice concern.  One example use of multipledispatch in SymPy is at #2979 .  

Best,
-Matt

Tim Lahey

unread,
Apr 1, 2014, 10:28:55 PM4/1/14
to sy...@googlegroups.com
I think that we'll be able to simplify some things and make some other
things possible by adding multiple dispatch. I'm for this.

Cheers,

Tim.

On 1 Apr 2014, at 22:10, Matthew Rocklin wrote:

> Hi Everyone,
>
> i'd like to add the
> multipledispatch<http://github.com/mrocklin/multipledispatch/> project
> #2979<https://github.com/sympy/sympy/pull/2979> .
>
>
> Best,
> -Matt
>
> --
> You received this message because you are subscribed to the Google
> Groups "sympy" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to sympy+un...@googlegroups.com.
> To post to this group, send email to sy...@googlegroups.com.
> Visit this group at http://groups.google.com/group/sympy.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/sympy/CAJ8oX-Gi8uGcXF6i73tGFp71%2B02mfJW2d42MUvADkRvCsZHRRw%40mail.gmail.com.
> For more options, visit https://groups.google.com/d/optout.

Brian Granger

unread,
Apr 1, 2014, 10:53:34 PM4/1/14
to sympy
I am strongly +1 on this idea.
> --
> You received this message because you are subscribed to the Google Groups
> "sympy" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to sympy+un...@googlegroups.com.
> To post to this group, send email to sy...@googlegroups.com.
> Visit this group at http://groups.google.com/group/sympy.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/sympy/CAJ8oX-Gi8uGcXF6i73tGFp71%2B02mfJW2d42MUvADkRvCsZHRRw%40mail.gmail.com.
> For more options, visit https://groups.google.com/d/optout.



--
Brian E. Granger
Cal Poly State University, San Luis Obispo
bgra...@calpoly.edu and elli...@gmail.com

Aaron Meurer

unread,
Apr 1, 2014, 11:09:45 PM4/1/14
to sy...@googlegroups.com
I think some questions should be answered first. I think we're all convinced of the technical merits, so I won't touch on those. 

What are your plans for multiple dispatch as a project?

Why can't this just be included in SymPy?

Do any other projects use multipledispatch, to your knowledge?

How stable is the API? Will we need to worry about compatibility breaks no version mismatches?

Aaron Meurer
--

Ondřej Čertík

unread,
Apr 2, 2014, 12:31:37 AM4/2/14
to sympy
On Tue, Apr 1, 2014 at 9:09 PM, Aaron Meurer <asme...@gmail.com> wrote:
> I think some questions should be answered first. I think we're all convinced
> of the technical merits, so I won't touch on those.
>
> What are your plans for multiple dispatch as a project?
>
> Why can't this just be included in SymPy?
>
> Do any other projects use multipledispatch, to your knowledge?
>
> How stable is the API? Will we need to worry about compatibility breaks no
> version mismatches?

I would like to see answers to those questions as well.

In general, I am ok with that.

Ondrej

Brian Granger

unread,
Apr 2, 2014, 12:41:39 AM4/2/14
to sympy
I strongly think that multipledispatch should be a separate project.
There is a good chance we would use this over time in IPython and I
definitely plan on using in various codes I write outside of SymPy.

It would probably be a good idea to get some good review done on
multipledispatch though before SymPy starts to rely on it.
> https://groups.google.com/d/msgid/sympy/-6231458978799167094%40unknownmsgid.
> For more options, visit https://groups.google.com/d/optout.



F. B.

unread,
Apr 2, 2014, 2:54:37 AM4/2/14
to sy...@googlegroups.com, elli...@gmail.com


On Wednesday, April 2, 2014 6:41:39 AM UTC+2, Brian Granger wrote:
I strongly think that multipledispatch should be a separate project.
There is a good chance we would use this over time in IPython and I
definitely plan on using in various codes I write outside of SymPy.

Maybe multiple dispatch should also be suggested to and shared with numpy. and other projects. So it would be possible to just import both libraries without function overriding.

In any case, sympy packages for windows could just include it, otherwise a pip dependence should be ok.

I'm +1 for the inclusion.

F. B.

unread,
Apr 2, 2014, 3:37:23 AM4/2/14
to sy...@googlegroups.com
By the way, what about using the test cases to tag the types of all of SymPy's methods by the @dispatch decorator?

That can be useful to try an automated translation to C++ or some other language.

Joachim Durchholz

unread,
Apr 2, 2014, 4:22:17 AM4/2/14
to sy...@googlegroups.com
Am 02.04.2014 04:10, schrieb Matthew Rocklin:
> As a reminder, SymPy currently doesn't depend on any external libraries, so
> this particular move also represents a policy shift. This potential policy
> shift follows substantial discussion, notably at
> https://github.com/sympy/sympy/issues/7339 . There are good reasons for
> and against external dependencies (I recommend checking out the header of
> the mentioned issue).
> I'm now specifically proposing that we depend on multipledispatch which
> might have other more specific benefits and drawbacks. Even if someone is
> for dependencies they might not be for this particular dependency.

1) On multidispatch itself: You can have either modularity or freeness
from surprises (this has been discussed before - essentially, if you
have modularity, adding a module with new MD definitions can change
dispatch decisions for related combinations of types).
This isn't a problem inside SymPy where we do not expect average users
to develop subclasses of existing, multi-dispatching SymPy object
classes. It might become a problem if multi-dispatch is made an
independent module.

2) On external dependencies: My recommendation is that an external
dependency is okay if and only if those parts of the API that we use are
stable. That means we need to evaluate how the project's culture handles
breaking API changes.
Some minor API changes might be tolerated, or if the project goes to
great pains to make the current API detectable to SymPy so it can issue
the right calls.

For MD, I think we should base any decisions on the question what will
happen if the project is abandoned (project maintainer lose interest,
are hit by a bus, whatever).
I'm leaning towards a model that SymPy can use that as an external
dependency, but be prepared to fork it for its own use as soon as the MD
project goes in a direction that's unsuitable for SymPy.

Jason Moore

unread,
Apr 2, 2014, 8:02:43 AM4/2/14
to sy...@googlegroups.com
Is it possible to move the main repository for mutlipledispatch under the sympy github org and grant the core sympy devs commit rights?
--
You received this message because you are subscribed to the Google Groups "sympy" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sympy+unsubscribe@googlegroups.com.

To post to this group, send email to sy...@googlegroups.com.
Visit this group at http://groups.google.com/group/sympy.

Matthew Rocklin

unread,
Apr 2, 2014, 10:58:49 AM4/2/14
to sy...@googlegroups.com
What are your plans for multiple dispatch as a project?

In terms of development I consider it mostly complete now.  Plans for the future are mostly performance tweaks and support for Python3 type annotations (Jeremiah Lowin has done some good work on this)
 
Why can't this just be included in SymPy?

It could be but it would be a fork and so might cause fragmentation 
 
Do any other projects use multipledispatch, to your knowledge?

I've used it in LogPy, my personal logic programming system.  It's a complex enough project to generate issues. SymPy would be the first major project with actual users though. 
 
How stable is the API? Will we need to worry about compatibility breaks no version mismatches?

I think that the API is pretty solid now but the project has really only had input from one developer.  I encourage others to take a look at the design docs and make suggestions sooner rather than later


Ondrej raised the API stability question on the issue.  I'll include my answer to that below

Yup, that's a valid concern. On the flip side once multipledispatch has users it's much harder for me to experiment and change things.

However, in this particular case there are a couple of things we can do.

  1. Multipledispatch supports a few interfaces, one of which is the interface used by singledispatch in Python 3.4 functools. It's safe to say that this interface is pretty stable
  2. Because I happen to develop both projects I can probably manage any changes simultaneously in the two codebases for the first few months

Finally, multipledispatch has a very small scope. I consider it to be fairly complete now. I mostly expect only performance tweaks and some Python 3 sugar in the future.

Matthew Rocklin

unread,
Apr 2, 2014, 10:59:42 AM4/2/14
to sy...@googlegroups.com
> It would probably be a good idea to get some good review done on
> multipledispatch though before SymPy starts to rely on it.

I also strongly encourage this.  Come on over to github.com/mrocklin/multipledispatch folks!  Free beer!



Matthew Rocklin

unread,
Apr 2, 2014, 11:03:11 AM4/2/14
to sy...@googlegroups.com
> By the way, what about using the test cases to tag the types of all of SymPy's methods by the @dispatch decorator?

Beware that multiply dispatched functions do incur a microsecond of overhead.  This does not come for free.

In [1]: from multipledispatch import dispatch

In [2]: def f(x):
   ...:     pass
   ...: 

In [3]: @dispatch(int)
   ...: def g(x):
   ...:     pass
   ...: 

In [4]: timeit f(1)
10000000 loops, best of 3: 83.5 ns per loop

In [5]: timeit g(1)
1000000 loops, best of 3: 1.04 µs per loop

This is roughly on the order of creating a Basic

In [6]: from sympy import Basic

In [7]: timeit Basic()
1000000 loops, best of 3: 589 ns per loop




On Wed, Apr 2, 2014 at 12:37 AM, F. B. <franz....@gmail.com> wrote:
By the way, what about using the test cases to tag the types of all of SymPy's methods by the @dispatch decorator?

That can be useful to try an automated translation to C++ or some other language.

--
You received this message because you are subscribed to the Google Groups "sympy" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sympy+un...@googlegroups.com.
To post to this group, send email to sy...@googlegroups.com.
Visit this group at http://groups.google.com/group/sympy.

Matthew Rocklin

unread,
Apr 2, 2014, 11:05:39 AM4/2/14
to sy...@googlegroups.com

1) On multidispatch itself: You can have either modularity or freeness from surprises (this has been discussed before - essentially, if you have modularity, adding a module with new MD definitions can change dispatch decisions for related combinations of types).
This isn't a problem inside SymPy where we do not expect average users to develop subclasses of existing, multi-dispatching SymPy object classes. It might become a problem if multi-dispatch is made an independent module.

Multipledispatch supports namespaces.  If someone wants to extend the sympy namespace they need to do

from sympy.dispatch import dispatch

@dispatch(MyClass)
def foo(x):
    ... 

So in general conflicts can happen but they're unlikely to happen by accident.
 

Ondřej Čertík

unread,
Apr 2, 2014, 11:09:19 AM4/2/14
to sympy
This is explained here:

http://multiple-dispatch.readthedocs.org/en/latest/design.html#namespaces-and-dispatch

As far as performance goes --- what are the bottlenecks? Is Julia's
multipledispatch the same fast?
If it is faster, can multipledispatch be made faster too?

Ondrej

Matthew Rocklin

unread,
Apr 2, 2014, 11:09:56 AM4/2/14
to sy...@googlegroups.com
It is possible to do either of these things, I'm against the first though.  Multiple dispatch is not a SymPy project.  It's as usable by numpy or IPython as by SymPy.

I'm happy to grant commit rights to anyone who actively develops the project.  Ondrej has been futzing with it recently.


To unsubscribe from this group and stop receiving emails from it, send an email to sympy+un...@googlegroups.com.

To post to this group, send email to sy...@googlegroups.com.
Visit this group at http://groups.google.com/group/sympy.

Ondřej Čertík

unread,
Apr 2, 2014, 11:15:03 AM4/2/14
to sympy
On Wed, Apr 2, 2014 at 9:09 AM, Matthew Rocklin <mroc...@gmail.com> wrote:
> It is possible to do either of these things, I'm against the first though.
> Multiple dispatch is not a SymPy project. It's as usable by numpy or
> IPython as by SymPy.
>
> I'm happy to grant commit rights to anyone who actively develops the
> project. Ondrej has been futzing with it recently.

One thing that sucks about github is that when you have the project
under your name, when you push in a development branch, everybody who
clones it will see it.
Maybe you can create a new organization for it. I don't know.

Ondrej

Matthew Rocklin

unread,
Apr 2, 2014, 11:18:32 AM4/2/14
to sy...@googlegroups.com

As far as performance goes --- what are the bottlenecks? Is Julia's
multipledispatch the same fast?
If it is faster, can multipledispatch be made faster too?

Julia does dispatch at compile time, we do it at runtime.  As implemented now this requires a dictionary lookup.  I can imagine some preformance improvements here, for example we could store the last called method in a field on the assumption that people often call the same function many times repeatedly in a for loop.

This is the sort of thing that could use an extra pair of eyes.  In general though there will be an overhead, I don't think that this overhead is super-significant relevant to many of the operations that we'll dispatch.  For example this relatively simple SymPy operation is 7000 times slower than a multipledispatch lookup.

In [22]: timeit simplify(x + x + y)
100 loops, best of 3: 7.23 ms per loop

Matthew Rocklin

unread,
Apr 2, 2014, 11:19:51 AM4/2/14
to sy...@googlegroups.com
In general I'm happy to do so.  I'd like to wait until people actually starts to gain a following/developers though.


--
You received this message because you are subscribed to the Google Groups "sympy" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sympy+un...@googlegroups.com.
To post to this group, send email to sy...@googlegroups.com.
Visit this group at http://groups.google.com/group/sympy.

F. B.

unread,
Apr 2, 2014, 11:23:09 AM4/2/14
to sy...@googlegroups.com


On Wednesday, April 2, 2014 5:09:19 PM UTC+2, Ondřej Čertík wrote:

As far as performance goes --- what are the bottlenecks? Is Julia's
multipledispatch the same fast?
If it is faster, can multipledispatch be made faster too?


Julia uses type inference and LLVM. I think that whenever the type can be unambiguously inferred, it simply creates a direct link to the function in the program's jit compiled machine code.

Maybe a smart usage of either PyPy or numba could reach that performance.

Ondřej Čertík

unread,
Apr 2, 2014, 11:39:26 AM4/2/14
to sympy
Right. So the whole work is done here:

https://github.com/mrocklin/multipledispatch/blob/master/multipledispatch/dispatcher.py#L77

def __call__(self, *args, **kwargs):
types = tuple([type(arg) for arg in args])
try:
func = self._cache[types]
except KeyError:
func = self.resolve(types)
self._cache[types] = func
return func(*args, **kwargs)

Correct? This seems optimal to me, once it gets cached. We can hardly
to better in Python.

The resolve() function looks quite optimal to me as well:

def resolve(self, types):
if types in self.funcs:
return self.funcs[types]

n = len(types)
for signature in self.ordering:
if len(signature) == n and all(map(issubclass, types, signature)):
result = self.funcs[signature]
return result
raise NotImplementedError()

The only thing I can think of is that you are doing O(n) loop over
self.ordering. This list can become big, right? Once it is resolved
once, it is cached in __call__(), so it probably is not a big deal.
Since you need to check subclasses, I think you have to loop over
everything, unless one can somehow prepare some stuff ahead of time.

The only slow part happens during the registration (i.e. import time),
which happens in the add() method:

def add(self, signature, func):
self.funcs[signature] = func
self.ordering = ordering(self.funcs)
amb = ambiguities(self.funcs)
if amb:
warn(warning_text(self.name, amb), AmbiguityWarning)
self._cache.clear()

If it is ambiguous, shouldn't it possibly raise an exception?

I think this is the meat of the algorithm, everything else is just
bells and whistles?

Ondrej

Matthew Rocklin

unread,
Apr 2, 2014, 11:43:54 AM4/2/14
to sy...@googlegroups.com
There is a fair amount of logic in the call to ambiguities.  Ordering also does a topological sort.  Those are algorithmic but are both done at import time.  

One optimization would be to have an even faster cache of size one stored in a field on the dispatcher.  This would avoid a dictionary lookup in the presumably common case of repeating the same function over lots of data.

But really someone should profile this stuff line by line.  I've done most of the optimization that seemed easy to me.  I suspect that others have other ideas.



Ondrej

--
You received this message because you are subscribed to the Google Groups "sympy" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sympy+un...@googlegroups.com.
To post to this group, send email to sy...@googlegroups.com.
Visit this group at http://groups.google.com/group/sympy.

Ondřej Čertík

unread,
Apr 2, 2014, 11:47:01 AM4/2/14
to sympy
On Wed, Apr 2, 2014 at 9:43 AM, Matthew Rocklin <mroc...@gmail.com> wrote:
> There is a fair amount of logic in the call to ambiguities. Ordering also
> does a topological sort. Those are algorithmic but are both done at import
> time.
>
> One optimization would be to have an even faster cache of size one stored in
> a field on the dispatcher. This would avoid a dictionary lookup in the
> presumably common case of repeating the same function over lots of data.

def __call__(self, *args, **kwargs):
types = tuple([type(arg) for arg in args])
try:
func = self._cache[types]
except KeyError:
func = self.resolve(types)
self._cache[types] = func
return func(*args, **kwargs)

Isn't self._cache already stored in a dispatcher? I don't understand
what you mean by
"even faster cache of size one stored in a field on the dispatcher."

>
> But really someone should profile this stuff line by line. I've done most
> of the optimization that seemed easy to me. I suspect that others have
> other ideas.

Of course, it would have to be benchmarked.

Ondrej
> https://groups.google.com/d/msgid/sympy/CAJ8oX-GQBM6PCbOtX1G9n94_nm745Sd48oKTB_ytjnVqMYHdhA%40mail.gmail.com.

Ronan Lamy

unread,
Apr 2, 2014, 11:47:56 AM4/2/14
to sy...@googlegroups.com
Le 02/04/14 16:23, F. B. a écrit :
numba won't work since it cannot deal with general-purpose code. In
principle, PyPy can JIT away the dispatch, but it probably requires some
work to tell the JIT how to do it.

Matthew Rocklin

unread,
Apr 2, 2014, 12:05:02 PM4/2/14
to sy...@googlegroups.com
By cache of size one I mean "store the last type/function pair as a tuple in a field directly on the dispatcher, not in a dictionary".  The reasoning is that perhaps a field lookup is faster than inspecting a dictionary.  


Ondřej Čertík

unread,
Apr 2, 2014, 12:34:21 PM4/2/14
to sympy
On Wed, Apr 2, 2014 at 10:05 AM, Matthew Rocklin <mroc...@gmail.com> wrote:
> By cache of size one I mean "store the last type/function pair as a tuple in
> a field directly on the dispatcher, not in a dictionary". The reasoning is
> that perhaps a field lookup is faster than inspecting a dictionary.

I see, so you are optimizing a dictionary lookup for the last
type/function. I doubt this would be significantly faster in Python,
it might
even be slower if you call it for a different type. I would keep it as it is.

Ondrej

Joachim Durchholz

unread,
Apr 2, 2014, 1:51:43 PM4/2/14
to sy...@googlegroups.com
Am 02.04.2014 17:05, schrieb Matthew Rocklin:
>>
>>
>> 1) On multidispatch itself: You can have either modularity or freeness
>> from surprises (this has been discussed before - essentially, if you have
>> modularity, adding a module with new MD definitions can change dispatch
>> decisions for related combinations of types).
>> This isn't a problem inside SymPy where we do not expect average users to
>> develop subclasses of existing, multi-dispatching SymPy object classes. It
>> might become a problem if multi-dispatch is made an independent module.
>
> Multipledispatch supports namespaces.

I'm not talking about name conflicts.

I'm talking about this:

>> adding a module with new MD definitions can change dispatch
>> decisions for related combinations of types).

I explained this months ago.
Seems I was talking to deaf ears.

Ah well.

Matthew Rocklin

unread,
Apr 2, 2014, 2:02:06 PM4/2/14
to sy...@googlegroups.com
Sorry Joachim, 

It sounds like you're talking about name/signature pair conflicts, which is also what I think I was talking about.  It's true that foreign code can change behavior of local code which as you correctly point out can lead to issues.  It can also lead to really solid code organization if done well.  Polymorphism in dynamic languages generally has this issue.

But perhaps you could point to your previous message?  It's not that you're talking to deaf ears, it's that you're talking to ears that have too many messages coming in and are connected to a head with a relatively short memory.



--
You received this message because you are subscribed to the Google Groups "sympy" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sympy+unsubscribe@googlegroups.com.

To post to this group, send email to sy...@googlegroups.com.
Visit this group at http://groups.google.com/group/sympy.

Joachim Durchholz

unread,
Apr 2, 2014, 4:21:13 PM4/2/14
to sy...@googlegroups.com
Am 02.04.2014 20:02, schrieb Matthew Rocklin:
> Sorry Joachim,
>
> It sounds like you're talking about name/signature pair conflicts,

Not sure.

> which is
> also what I think I was talking about.

What I'm talking about can't be solved via namespaces.

> It's true that foreign code can
> change behavior of local code which as you correctly point out can lead to
> issues.

Yes.

> It can also lead to really solid code organization if done well.

No.

> Polymorphism in dynamic languages generally has this issue.

No.

> But perhaps you could point to your previous message?

I can't, it's been quite a while.

Basically, the issue is this:

Have a multidispatched function f(A,B). Actually, there are also
subtypes going on, say, A1, A2, and A3 for A and B1 and B2 for B.
The author of f carefully made sure that each combination of A, A1, A2,
A3, B, B1, and B2 would dispatch to the proper version of f.

Now have some independent author design a new subtype of A, say A'. It
could be a subtype of A, A1, A2, or A3, doesn't matter for the problem;
anyway that author defined f(A', B), f(A', B1), and f(A', B2), or at
least verified that the dispatch goes to the right version of f so that
each combination of A' with B/B1/B2 is handled correctly. (Imagine
subclasses that rip out the entire implementation and do something weird
and wonderful.)

Have a third author who does B', along the same lines.

The author of f isn't aware of the authors of A' and B'.
The authors of A' and B' are not aware of each other.

Now... who writes the unit test for f(A',B')?
That would be relevant even if one of the existing implementations of f
would fit that case, you'd still have to verify that the one that's
chosen by the MD mechanism will work.

Essentially, for a two-parameter function, you have a matrix where on
one axis you have A and all subclasses, and B and subclasses on the
others; the issue arises that anybody who extends the A hierarchy will
know to add a row to the B axis, and anybody who extends the B hierarchy
will know to add a row to the A axis, but there's nobody who's
responsible for filling the new corner.


Strategy #2 is fallbacks. You don't have an explicit entry for f(A',B'),
fine, first search superclasses of A', then of B', until you find an
implementation. The problem here is that you can have a total order over
single values, but you cannot have one over independent pairs (cf.
vectors and complex numbers, both can only have equality but not an
order - i.e. we're dealing with a very fundamental difference between
single dispatch and multiple dispatch).
The consequence is that depending on what the search order is, adding a
new implementation of f for any intermediate A',B' might (or may not)
change the dispatch decisions on f for A'' and B''. You can have cases
where you have a type B'', somebody adds an f(A',B), and suddenly
f(A'',B'') goes to the newly defined function instead of what you were
used to (say, f(A',B)). More complex scenarios can happen, with
consequently even more surprising behaviour.
So it's unmodular, in the form of surprising behaviour if you combine
arbitrary modules.

Strategy #2 is no fallbacks, instead, just report an error.
So if you have f(A',B) and f(A,B'), enforce the existence f(A',B') as well.
The check could be postponed to runtime (the issue won't ever surface if
there will never be an actual call to f with A' and B').
You'll need to allow defining such extensions outside the classes.
Consider again the case where the authors of A' and B' are not aware of
each other, so their modules will never define f(A',B'), this needs to
be done by whoever uses both A' and B', probably after seeing the error
message.
Again, this is unmodular, you just get an error message instead of
surprising behaviour.

Matthew Rocklin

unread,
Apr 2, 2014, 4:32:01 PM4/2/14
to sy...@googlegroups.com
The multipledispatch project detects such ambiguous situations and raises a warning when the ambiguity is created/detected (at import time).  If the developers don't do anything about it then multipledispatch selects one of the implementations to take precedence pseudo-randomly.


If you're interested in the code that does this please see https://github.com/mrocklin/multipledispatch/blob/master/multipledispatch/conflict.py

Pull requests welcome.

Aaron Meurer

unread,
Apr 2, 2014, 4:35:54 PM4/2/14
to sy...@googlegroups.com
Why does multipledispatch use issubclass instead of __mro__? What happens when you dispatch on a diamond inheritance? 

Aaron Meurer


--
You received this message because you are subscribed to the Google Groups "sympy" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sympy+un...@googlegroups.com.

To post to this group, send email to sy...@googlegroups.com.
Visit this group at http://groups.google.com/group/sympy.

Joachim Durchholz

unread,
Apr 2, 2014, 4:56:21 PM4/2/14
to sy...@googlegroups.com
Am 02.04.2014 22:32, schrieb Matthew Rocklin:
> The multipledispatch project detects such ambiguous situations and raises a
> warning when the ambiguity is created/detected (at import time).

Ah, that's good.

> If the
> developers don't do anything about it then multipledispatch selects one of
> the implementations to take precedence pseudo-randomly.

Hm. I'm not sure whether that's a good or a bad idea.

It should be deterministic and depend only on parameter types.
Or whatever needs to be done to make unit tests choose the same dispatch
decisions as production code.
So... a hash on parameter types would probably be okay, a PRNG that's
seeded once definitely is not.

But it's a corner case.

Matthew Rocklin

unread,
Apr 2, 2014, 4:58:06 PM4/2/14
to sy...@googlegroups.com
The multipledispatch project does not think hard about multiple inheritance in an intelligent way.  It uses issubclass in order to support abstract classes like Iterator and Number.


Aaron Meurer

unread,
Apr 2, 2014, 4:58:21 PM4/2/14
to sy...@googlegroups.com
Yeah, any project that you intend to be community based (rather than your own project) you should put under an organization. It looks like the GitHub username multipledispatch is not taken. Maybe you should grab it up. 

Aaron Meurer


--
You received this message because you are subscribed to the Google Groups "sympy" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sympy+un...@googlegroups.com.
To post to this group, send email to sy...@googlegroups.com.
Visit this group at http://groups.google.com/group/sympy.

Matthew Rocklin

unread,
Apr 2, 2014, 4:59:32 PM4/2/14
to sy...@googlegroups.com
Sorry, I used pseudo-random loosely.  We currently use hash by default although this could easily be changed.  See the tie_breaker keyword argument to the edge function in conflict.py. https://github.com/mrocklin/multipledispatch/blob/master/multipledispatch/conflict.py


--
You received this message because you are subscribed to the Google Groups "sympy" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sympy+unsubscribe@googlegroups.com.
To post to this group, send email to sy...@googlegroups.com.
Visit this group at http://groups.google.com/group/sympy.

Aaron Meurer

unread,
Apr 2, 2014, 4:59:20 PM4/2/14
to sy...@googlegroups.com
Also if you use __mro__, wouldn't you not have to build up some topological graph? Just pick the dispatched class that comes last in the mro.

Aaron Meurer


Matthew Rocklin

unread,
Apr 2, 2014, 5:02:40 PM4/2/14
to sy...@googlegroups.com
The first incarnation of multipledispatch used mro.  It worked.  The code to detect ambiguities and select between multiple implementations in the multiple input case was fairly involved.  I'm pretty happy with the abstractions behind the current setup.  As always though pull requests welcome.  The fact that we don't support multiple inheritance well is a failing and a potential source of hair pulling down the line.


Aaron Meurer

unread,
Apr 2, 2014, 5:03:07 PM4/2/14
to sy...@googlegroups.com
Wouldn't it be better for the default tiebreaker to just raise an exception?

Aaron Meurer


To unsubscribe from this group and stop receiving emails from it, send an email to sympy+un...@googlegroups.com.

To post to this group, send email to sy...@googlegroups.com.
Visit this group at http://groups.google.com/group/sympy.

Aaron Meurer

unread,
Apr 2, 2014, 5:04:15 PM4/2/14
to sy...@googlegroups.com
And going back to my original comments, wouldn't changing tie breaking or mro schemes down the line be API breaks?

Aaron Meurer


Matthew Rocklin

unread,
Apr 2, 2014, 5:10:43 PM4/2/14
to sy...@googlegroups.com
Not all of the ties result from ambiguities.  For example the signatures (int,) and (float,) tie.  Which should come first in the toposort?

Regarding API breaks I recommend that SymPy not inject any ambiguities.  These can be avoided just by adding extra function definitions.  E.g.

@dispatch(object, float)
def f(x, y):
    return 1

@dispatch(float, object)
def f(x, y):
    return 2

f(1.0, 2.0)  # oh no! which do we choose?

In this case multipledispatch gives you a warning when you register the second implementation

multipledispatch/core.py:52: AmbiguityWarning:

Ambiguities exist in dispatched function f

The following signatures may result in ambiguous behavior:
    [float, object], [object, float]


Consider making the following additions:

    @dispatch(float, float)
    def f(...)

My answer to this problem is that we just follow the instructions and define functions to cover all ambiguities.


Aaron Meurer

unread,
Apr 2, 2014, 6:20:33 PM4/2/14
to sy...@googlegroups.com
On Wed, Apr 2, 2014 at 4:10 PM, Matthew Rocklin <mroc...@gmail.com> wrote:
Not all of the ties result from ambiguities.  For example the signatures (int,) and (float,) tie.  Which should come first in the toposort?

For what input?
 

Regarding API breaks I recommend that SymPy not inject any ambiguities.  These can be avoided just by adding extra function definitions.  E.g.

@dispatch(object, float)
def f(x, y):
    return 1

@dispatch(float, object)
def f(x, y):
    return 2

f(1.0, 2.0)  # oh no! which do we choose?

I would definitely raise an exception here. You can maybe play with ways for people to tell the dispatcher how they want it to break ties, but the default implementation shouldn't take sides.
 

In this case multipledispatch gives you a warning when you register the second implementation

Couldn't you resolve the ambiguity by adding additional dispatch against (float, float)? I don't think it's right to do this check at registry time. 
 

multipledispatch/core.py:52: AmbiguityWarning:

Ambiguities exist in dispatched function f

The following signatures may result in ambiguous behavior:
    [float, object], [object, float]


Consider making the following additions:

    @dispatch(float, float)
    def f(...)

My answer to this problem is that we just follow the instructions and define functions to cover all ambiguities.

I agree 100%, but in the face of ambiguity, refuse the temptation to guess.

Aaron Meurer
 

Matthew Rocklin

unread,
Apr 2, 2014, 6:33:51 PM4/2/14
to sy...@googlegroups.com
Not all of the ties result from ambiguities.  For example the signatures (int,) and (float,) tie.  Which should come first in the toposort?

For what input?

How MD operates now is that it forms a graph of signatures with an edge between two signatures if one is more specific than the other.  It then topologically sorts this graph to form a linear ordering.  When inputs come in we get their types and then compare those types against each of the type signatures in the linear ordering from most to least specific (by the topological sort).  Once we find a match we return the function that was registered with the matching signature.  In this way we get to use issubclass and are also assured that we aren't missing a more specific signature.

So, in the float/int case our graph is just two disconnected nodes and our ordering might look like 

[(int,), (float,)]

When an input comes in, like 1.0, we get it's types, (float,), and then ask issubclass(float, int), get no, and move on to ask if issubclass(float, float).  This works so we return the function associated with (float,).

(Although actually we first just check in a dict to see if we have the type signature exactly, which we do in this case, so we return in constant time)
 
I would definitely raise an exception here. You can maybe play with ways for people to tell the dispatcher how they want it to break ties, but the default implementation shouldn't take sides.

I can see the reasoning here but slightly disagree.  Often both implementations are perfectly valid and you should just pick one.  This is also consistent with the standard set out by the Julia Language, from which I'm stealing a good amount of inspiration.

Couldn't you resolve the ambiguity by adding additional dispatch against (float, float)?

Yes, this is what is suggested automatically by MD.  Perhaps I don't understand your comment.
 
I don't think it's right to do this check at registry time. 

I'm not sure that I understand this either.  When would you do the check?

Matthew Rocklin

unread,
Apr 2, 2014, 6:42:00 PM4/2/14
to sy...@googlegroups.com
If it is possible to catch warnings then we could catch an MD warning and raise an error in SymPy if we wanted.  My PRs introducing MD into SymPy include a tiny sympy.dispatch module rather than using MD directly.  There is little stopping us from tailoring some of the behavior to fit SymPy's priorities.

Ondřej Čertík

unread,
Apr 2, 2014, 7:34:28 PM4/2/14
to sympy

I already asked this, but didn't get a reply: I really think it should raise an exception on ambiguity by default. You can allow the user to explicitly override this behavior if you know what you are doing.

Sent from my mobile phone.

--
You received this message because you are subscribed to the Google Groups "sympy" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sympy+un...@googlegroups.com.
To post to this group, send email to sy...@googlegroups.com.
Visit this group at http://groups.google.com/group/sympy.

Matthew Rocklin

unread,
Apr 2, 2014, 8:57:28 PM4/2/14
to sy...@googlegroups.com

Ah, must have missed it, fortunately Aaron bright up the same point. My response:

I can see the reasoning here but slightly disagree.  Both implementations are presumably valid and you should just pick one.  This is also consistent with the standard set out by the Julia Language, from which I'm stealing a good amount of inspiration.

Perhaps it would make sense to reach out to the Julia folks and ask about their experiences.

And then I suggest that sympy could catch warnings and throw errors.  In my PRs I use a small module sympy. dispatch rather than the raw dispatch in multipledispatch

Aaron Meurer

unread,
Apr 2, 2014, 9:45:00 PM4/2/14
to sy...@googlegroups.com
On Wed, Apr 2, 2014 at 5:33 PM, Matthew Rocklin <mroc...@gmail.com> wrote:
Not all of the ties result from ambiguities.  For example the signatures (int,) and (float,) tie.  Which should come first in the toposort?

For what input?

How MD operates now is that it forms a graph of signatures with an edge between two signatures if one is more specific than the other.  It then topologically sorts this graph to form a linear ordering.  When inputs come in we get their types and then compare those types against each of the type signatures in the linear ordering from most to least specific (by the topological sort).  Once we find a match we return the function that was registered with the matching signature.  In this way we get to use issubclass and are also assured that we aren't missing a more specific signature.

So, in the float/int case our graph is just two disconnected nodes and our ordering might look like 

[(int,), (float,)]

When an input comes in, like 1.0, we get it's types, (float,), and then ask issubclass(float, int), get no, and move on to ask if issubclass(float, float).  This works so we return the function associated with (float,).

(Although actually we first just check in a dict to see if we have the type signature exactly, which we do in this case, so we return in constant time)

My point is, the order comes from the input itself, not from the registry. AKA, you should use MRO. MRO satisfied monotonicity, which I think for multiple dispatch basically says that if you subclass a class, it won't change which function it will dispatch to (see https://www.python.org/download/releases/2.2.3/descrintro).
 
 
I would definitely raise an exception here. You can maybe play with ways for people to tell the dispatcher how they want it to break ties, but the default implementation shouldn't take sides.

I can see the reasoning here but slightly disagree.  Often both implementations are perfectly valid and you should just pick one.  This is also consistent with the standard set out by the Julia Language, from which I'm stealing a good amount of inspiration.

I think Julia only has one use-case in mind, which is that a function is going to compute something, dispatched against some algorithm, and return the result. But I think there are other possibilities. 

I'll think about some concrete examples, but my gut tells me that there are going to be different ways that people might want to break or not break ties. Multipledispatch should allow that via subclassing the Dispatch class, and the default implementation should not do it. I also reiterate

In the face of ambiguity, refuse the temptation to guess.



Couldn't you resolve the ambiguity by adding additional dispatch against (float, float)?

Yes, this is what is suggested automatically by MD.  Perhaps I don't understand your comment.

The point is, you are warning when the function is registered. If you have

 @dispatch(float, object)
def add(a, b):
...
@dispatch(object, float)
def add(a, b):
...
@dispatch(float, float)
def add(a, b):
...

it will raise a warning telling you to do something that you have already done. Since there's no way to "finalize" the dispatcher (nor should there be), it should just do this at run time. You can cache the state of the Dispatch object if performance is a concern.

 
I don't think it's right to do this check at registry time. 

I'm not sure that I understand this either.  When would you do the check?

When the function is called. See what I wrote above. Also there's no reason that you couldn't register more functions later.

I haven't yet actually read the code, only the documentation and what you wrote here, so if I wrote something stupid above, pardon it.

Aaron Meurer
 

--
You received this message because you are subscribed to the Google Groups "sympy" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sympy+un...@googlegroups.com.
To post to this group, send email to sy...@googlegroups.com.
Visit this group at http://groups.google.com/group/sympy.

Aaron Meurer

unread,
Apr 2, 2014, 9:46:39 PM4/2/14
to sy...@googlegroups.com
If you use the warnings module, you can convert warnings into exceptions easily enough, but I would just allow to specify it in the Dispatch object. As I noted in the previous email, it would be nice to subclass Dispatch to have different tie breaking schemes. You could just have Dispatch(warn=True) and Dispatch(raise_=True) or WarningDispatch and RaisingDispatch or whatever.

Aaron Meurer


--
You received this message because you are subscribed to the Google Groups "sympy" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sympy+un...@googlegroups.com.
To post to this group, send email to sy...@googlegroups.com.
Visit this group at http://groups.google.com/group/sympy.

Ondřej Čertík

unread,
Apr 2, 2014, 10:16:01 PM4/2/14
to sympy

Ok. I am installing Julia and I play with it more. Julia argument is a good argument because Julia is a solid iterative improvement over Python. I think in the next 5 years Python will be used a lot more than Julia and it's hard to predict what will happen after that. But I think that Julia is going to be used a lot, so we should try to be compatible if it makes sense.

Sent from my mobile phone.

Matthew Rocklin

unread,
Apr 3, 2014, 1:38:15 AM4/3/14
to sy...@googlegroups.com
My point is, the order comes from the input itself, not from the registry. AKA, you should use MRO. MRO satisfied monotonicity, which I think for multiple dispatch basically says that if you subclass a class, it won't change which function it will dispatch to (see https://www.python.org/download/releases/2.2.3/descrintro).

I think that the current system also satisfies this condition.  Perhaps this discussion would be more concrete with an example test that multipledispatch fails?
 
I would definitely raise an exception here. You can maybe play with ways for people to tell the dispatcher how they want it to break ties, but the default implementation shouldn't take sides.

I can see the reasoning here but slightly disagree.  Often both implementations are perfectly valid and you should just pick one.  This is also consistent with the standard set out by the Julia Language, from which I'm stealing a good amount of inspiration.

I think Julia only has one use-case in mind, which is that a function is going to compute something, dispatched against some algorithm, and return the result. But I think there are other possibilities. 

I think that Julia and Python target very similar application spaces here.  I don't see a distinction.
 
I'll think about some concrete examples, but my gut tells me that there are going to be different ways that people might want to break or not break ties. Multipledispatch should allow that via subclassing the Dispatch class, and the default implementation should not do it. I also reiterate

In the face of ambiguity, refuse the temptation to guess.

That's a reasonable philosophy.  In this case though we actually have two (or more) perfectly valid implementations.  Question, what does Python do when ambiguities arise in the multiple inheritance case?  I think that it chooses based on the order in which the classes appeared in the class definition.  This is fairly arbitrary. We still do it though.  I understand that you express a slight preference when stating the order but still, it's a pretty weak signal on which to decide things (IMO).

Couldn't you resolve the ambiguity by adding additional dispatch against (float, float)?

Yes, this is what is suggested automatically by MD.  Perhaps I don't understand your comment.

The point is, you are warning when the function is registered. If you have

 @dispatch(float, object)
def add(a, b):
...
@dispatch(object, float)
def add(a, b):
...
@dispatch(float, float)
def add(a, b):
...

it will raise a warning telling you to do something that you have already done. Since there's no way to "finalize" the dispatcher (nor should there be), it should just do this at run time. You can cache the state of the Dispatch object if performance is a concern.

I think that it's valuable to get this information when you write your code.  If you get no warnings when you load up your code then you know that there won't be an issue.  I also suspect that runtime checking will be costly.
 
I haven't yet actually read the code, only the documentation and what you wrote here, so if I wrote something stupid above, pardon it.

Nothing stupid as far as I can see.  These are all legitimate concerns. This is a challenging problem though and I don't think that it's possible to satisfy all concerns simultaneously.

Also, I do recommend actually reading through the module.  It's very short (~300 LOC most of which are comments) and I think that reading it might give you more insight into what happens now.  If you walk back a bit in the git history you can come across an old version that did everything at runtime using mro.  My personal release notes say that this happened before the 0.2.0 version bump.

Here is a question: which of these issues block SymPy's use of multiple dispatch?  From what I can see none of these strongly impact the API, particularly not if we enforce a "don't let dispatch ambiguities slide" policy.  I think that the warnings are sufficiently loud to make enforcement of this policy trivial.  I also don't think that it's going to be a big deal, but again, this is something to ask the Julia folks.

Joachim Durchholz

unread,
Apr 3, 2014, 3:52:38 AM4/3/14
to sy...@googlegroups.com
Am 02.04.2014 23:10, schrieb Matthew Rocklin:
> multipledispatch/core.py:52: AmbiguityWarning:
>
> Ambiguities exist in dispatched function f
>
> The following signatures may result in ambiguous behavior:
> [float, object], [object, float]

Excellent diagnostics.

> Consider making the following additions:
>
> @dispatch(float, float)
> def f(...)

Excellent advice to developers.

> My answer to this problem is that we just follow the instructions and
> define functions to cover all ambiguities.

+1.

Joachim Durchholz

unread,
Apr 3, 2014, 3:54:49 AM4/3/14
to sy...@googlegroups.com
Am 02.04.2014 23:02, schrieb Matthew Rocklin:
> The first incarnation of multipledispatch used mro. It worked. The code
> to detect ambiguities and select between multiple implementations in the
> multiple input case was fairly involved. I'm pretty happy with the
> abstractions behind the current setup. As always though pull requests
> welcome. The fact that we don't support multiple inheritance well *is* a
> failing and a potential source of hair pulling down the line.

MRO itself is a potential source of hair pulling down the line.
(I'm not aware of any programming language that cleanly solves diamond
inheritance.)

The question is whether the trade-offs involved in the MRO algorithm
match the purpose of multiple dispatch.
I *think* the issues are orthogonal and MRO should be used, but I'm not
100% sure.

Why was the MRO-using code so involved?
I'd have thought that whatever order you're using, having a less-than
comparison should be enough to code multidispatch. Does Python's MRO
implementation not offer a less-than comparison?

Joachim Durchholz

unread,
Apr 3, 2014, 4:00:23 AM4/3/14
to sy...@googlegroups.com
Am 02.04.2014 23:03, schrieb Aaron Meurer:
> Wouldn't it be better for the default tiebreaker to just raise an exception?

Personally, I'd lean for an exception myself, but I can imagine use
cases where a warning would be more appropriate.

I'm not sure how to put that kind of choice in though. The code that
should decide this is the caller, but it would burden each call site
with having to make that decision. One could configure that decision on
a per-package basis, but that would complicate and slow down the
dispatch itself.

Joachim Durchholz

unread,
Apr 3, 2014, 4:18:27 AM4/3/14
to sy...@googlegroups.com
Am 03.04.2014 07:38, schrieb Matthew Rocklin:
> Question, what does Python do
> when ambiguities arise in the multiple inheritance case? I think that it
> chooses based on the order in which the classes appeared in the class
> definition. *This *is fairly arbitrary. We still do it though. I
> understand that you express a slight preference when stating the order but
> still, it's a pretty weak signal on which to decide things (IMO).

Agreed, but Python's MRO is a hack anyway. The more subtleties of the
multiple inheritance situation you explore, the more failure conditions
you find for MRO.
MRO is a reaonably good solution for 99% of the cases. 100% solutions do
exist but Python did not choose them (nor did any other language that
I'm aware of).

>> The point is, you are warning when the function is registered. If you have
>>
>> @dispatch(float, object)
>> def add(a, b):
>> ...
>> @dispatch(object, float)
>> def add(a, b):
>> ...
>> @dispatch(float, float)
>> def add(a, b):
>> ...
>>
>> it will raise a warning telling you to do something that you have already
>> done. Since there's no way to "finalize" the dispatcher (nor should there
>> be), it should just do this at run time. You can cache the state of the
>> Dispatch object if performance is a concern.
>
> I think that it's valuable to get this information when you write your
> code. If you get no warnings when you load up your code then you know that
> there won't be an issue. I also suspect that runtime checking will be
> costly.

Just fill the matrix slot with a lambda that issues the warning, then
calls whatever function should be called.
The warning-issuing code should probably also replace the matrix slot
with that "whatever function", to avoid spamming stderr with potentially
a gazillion of ambiguity warnings.

> Here is a question: which of these issues block SymPy's use of multiple
> dispatch? From what I can see none of these strongly impact the API,
> particularly not if we enforce a "don't let dispatch ambiguities slide"
> policy. I think that the warnings are sufficiently loud to make
> enforcement of this policy trivial. I also don't think that it's going to
> be a big deal, but again, this is something to ask the Julia folks.

One approach might be to have a policy that SymPy should cover the full
matrix (i.e. make sure that the warning never happens via a unit test
for each slot in the matrix), but stick with warnings.

The issue is that our users might fail to fill all matrix slots but it's
not an error because whatever default implementation MD chooses is fine.

BTW that use case makes me dislike the "dispatch to random in case of
ambiguities" policy more.
Yeah it's unclean to guess, and yeah library code should be prevented
from abusing that MD doesn't burn and crash, but I do not see that
application code needs to adhere to such a strict policy. They will
still get that annoying warning, which is good enough to have them fill
the slot.

F. B.

unread,
Apr 3, 2014, 7:11:48 AM4/3/14
to sy...@googlegroups.com


On Thursday, April 3, 2014 10:18:27 AM UTC+2, Joachim Durchholz wrote:

MRO is a reaonably good solution for 99% of the cases. 100% solutions do
exist but Python did not choose them (nor did any other language that
I'm aware of).


The latest edition of Java, i.e. Java 8 supports multiple inheritance by adding default methods to interfaces. Any ambiguities are checked at compile-time and raise an error. A subclass inheriting two interfaces with the same method has to override it or specify which one to use.

Joachim Durchholz

unread,
Apr 3, 2014, 10:20:03 AM4/3/14
to sy...@googlegroups.com
Am 03.04.2014 13:11, schrieb F. B.:
>
> The latest edition of Java, i.e. Java 8 supports multiple inheritance by
> adding default methods to interfaces.

Right, that's something that touches on the subject.

> Any ambiguities are checked at
> compile-time and raise an error.

Ambiguities are just scratching the surface of potential
multiple-inheritance issues :-)

Raising an error... hm, well, it's better than nothing but solidly in
the 80% bracket as far as solutions go. IMNSHO.

Matthew Rocklin

unread,
Apr 3, 2014, 10:36:59 AM4/3/14
to sy...@googlegroups.com
> Why was the MRO-using code so involved?

No particular recollection on this.  It could also be that it was just done before I understood the problem as well. 

> I'd have thought that whatever order you're using, having a less-than comparison should be enough to code multidispatch. Does Python's MRO implementation not offer a less-than comparison?

The less than comparison that I use is issubclass (actually less than or equal to).  To me this seems like a more natural way to ask less than relations between types than MRO.



--
You received this message because you are subscribed to the Google Groups "sympy" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sympy+unsubscribe@googlegroups.com.

To post to this group, send email to sy...@googlegroups.com.
Visit this group at http://groups.google.com/group/sympy.

Matthew Rocklin

unread,
Apr 3, 2014, 10:53:11 AM4/3/14
to sy...@googlegroups.com
But again, the codebase is small.  You're going to have a tough time convincing me to change back to using MRO over issubclass.  You're also going to have a tough time convincing me to work to switch to runtime errors rather than import time errors.  However it should be fairly easy for a decently skilled developer to implement these ideas, test them, and demonstrate that speed is not sacrificed and that important use cases are better handled using MRO and runtime errors.  

I'm not yet convinced but I'm not set against these ideas.  I think that the next logical step is for someone who isn't me to do something concrete.  This will also force someone else to go through and validate or raise issues on the codebase which is needed anyway (no project exists before two pairs of eyes has seen it.)

Given that SymPy probably shouldn't accept any ambiguities I think that many of the issues here, while important, should not block progress.

Something I am happy to do is to break out the ambiguity response code to a keyword argument.  This is a fairly arbitrary choice and so it would be good to provide hooks.  In this way it should be easy for SymPy to raise errors instead of warnings when ambiguities are detected.  

Matthew Rocklin

unread,
Apr 3, 2014, 11:17:49 AM4/3/14
to sy...@googlegroups.com
I add an on_ambiguity callback in https://github.com/mrocklin/multipledispatch/pull/12
Reply all
Reply to author
Forward
0 new messages