
Five-minute Multimethods in Python - llambda
http://www.artima.com/weblogs/viewpost.jsp?thread=101605
======
tikhonj
The definition of multimethods presented here sounds suspiciously like ad-hoc
polymorphism (or just overloading functions). It seems like the real
difficulty is not in the concept but in the fact that I've now seen under
three different names: multiple-dispatch, ad-hoc polymorphism and overloading.
All but the last name _seem_ complex; since I initially encountered the
concept in Java as overloading, I never thought much about it.

Polymorphism based on types like this is something that statically typed
languages are naturally good at. Looking at his example code, the trick to
having this sort of code in dynamically typed Python is to introduce type
annotations via decorators. You're doing the work of static typing for a
fraction of the benefits.

Additionally, a statically typed language can take this concept even further.
Haskell, for example, supports polymorphic functions like this very well using
type classes. It even allows functions polymorphic on their _return_ types--
you can have a function `read` which takes a String and returns whatever. This
means you can actually even have polymorphic _constants_ \--maxBound, for
example, is the maximum value of whatever numerical representation it happens
to be.

Overall, I think features like this make more sense in a statically typed
language. They fit in--you're already specifying types everywhere--and can do
more. However, it is interesting to see how you could accomplish something
like this in a dynamically typed language.

~~~
jules
Multimethods and overloading are completely different beasts, despite the
syntactic similarity. Overloading is resolved at compile time by looking at
the compile time types. Multimethods are dispatched at run time, by looking at
the run time values. This makes overloading much less powerful. Multimethods
are in fact problematic in statically typed languages; they are difficult (but
not impossible) to type check.

~~~
barrkel
They are only completely different beasts if you think the distinction between
compile time and run time is very important. That's not necessarily the case.
There is the text of the program at one end, and pushing electrons at the
other. At exactly which point this thing we call "compiling" occurred is just
a detail that may or may not help us in other - typically human factor -
goals.

Multimethods, to a first approximation, is overload resolution at run time
rather than compile time. They are not just syntactically similar; they are
semantically similar too. Multimethods is the dynamic language analogue to
overloading in static languages.

~~~
riffraff
> Multimethods is the dynamic language analogue to overloading in static
> languages.

This implies that multimethods are not possible in statically typed languages,
which is trivially disproven by stuff like the Nice programming language.

The difference between compile and run time is a red herring, since the actual
distinction is between a statically _declared_ type and a dynamic type (which
FWIW could be determined statically within AOT compilation).

~~~
barrkel
No, it doesn't imply that, because static and dynamic are not mutually
exclusive. A static language with polymorphism[1] has both static and dynamic
aspects. A purely static language does not need (and thus won't have) runtime
type tags of any kind, so any kind of dynamic dispatch (including
multimethods) would be impossible at the level of the language (i.e. you'd
have to implement it all explicitly as a library).

[1] It shouldn't need to be said, but this is HN so it does need to be said,
but I mean OO-style runtime polymorphism here (or, with a stretch, sum types),
not parametric polymorphism.

~~~
igouy
Why haven't you told us what you mean by the notoriously vague terms "static"
and "dynamic"?

~~~
barrkel
Because defining every word one uses in a forum post is incredibly tedious,
and even when you try to do so preemptively, someone still comes in and asks
you for definitions of more words.

I'm using them in a colloquial rather than rigorous sense, because that's the
lowest friction way of communicating in a casual setting.

But at core, when I say 'static', I mean something determined before
execution, by means of static scopes and compile-time types; when I say
dynamic, I mean something determined at the point of execution, by means of
dynamic scopes and run-time values. When I say 'static scopes', I mean scopes
determined through lexical nesting and static types. When I say 'static type',
I mean types determined without need for any execution of the target program.
When I say 'dynamic scopes', I mean scopes determined through dictionary
lookup at runtime (vtables are dictionaries too), possibly even including
dynamic scopes (cf Lisp, Javascript eval, etc.).

Most languages have both dynamic and static aspects, but the mixes vary
greatly. Languages designed to be compiled typically use far more statically
resolved features, because if you can afford the time to put into analysis,
and you have all the source immediately available, you can find a lot of
common bugs and generate faster code. Languages designed to be interpreted
typically use far more dynamically resolved features, because that gives less
latency to execution, more expressiveness, more flexibility with modularity
and program composition, and is also easier to implement.

~~~
igouy
The difficulty is not with understanding that _"when I say 'static', I mean
something determined before execution"_ but that you only say 'static' - you
don't tell use what is being determined before execution, and that turns your
comments into meaningless mush ;-)

------
andrewcooke
pytyp will also do this, if anyone is interested -
<http://acooke.org/pytyp/pytyp.spec.dispatch.html>

(in addition, it can be used to add type checking, and type-guided translation
between json and python objects. it will also dispatch on "compound" types,
like a list of integers).

------
agentultra
<http://en.wikipedia.org/wiki/Multiple_dispatch#Common_Lisp>

I tend to prefer the (defmethod) form in CL. You get generic functions which
can retrieve the function object specialized for the parameters with (find-
method). You also get the auxiliary method goodness...

But the great thing with Python that the article shows is that there's
probably a way you can get those things in Python too if you needed them.

So does Python _have_ multiple dispatch or can you just implement it?

~~~
swannodette
The challenge with expressive features like multimethods - does your language
give you the tools not only to implement it, but to make it fast? This is yet
another benefit of macros - the ability to add features like this and get
good, perhaps even great, performance.

------
ianb
Since people are throwing out similar kinds of implementations, here's my
pattern matching library (which is kind of the same thing as multimethods, but
allowing more arbitrary matching than just based on type):

<http://svn.colorstudy.com/home/ianb/recipes/patmatch.py>

------
chadrs
I did something similar and tried to implement functional language style
pattern matching in python:

<https://gist.github.com/1320421>

Not to actually use, of course.

------
daenz
This is kind of crazy. I had written pypolymorph independently a few months
ago that does this same thing: <https://github.com/amoffat/pypolymorph>

------
antihero
"a function that has multiple versions, distinguished by the type of the
arguments"

Ooh, C# style overloading.

~~~
Anchor
Yes, but not quite. Consider the following example:

    
    
      static void Main()
      {
        Bar( 12, 15 );
      }
    
      static void Bar( object o1, object o2 )
      {
        Foo( o1, o2 ); // error
      }
    
      static void Foo( int x, double y )
      {
        Console.WriteLine( "int double" );
      }
    
      static void Foo( double x, int y )
      {
        Console.WriteLine( "double int" );
      }
    
      static void Foo( double x, double y )
      {
        Console.WriteLine( "double double" );
      }
    
      static void Foo( int x, int y )
      {
        Console.WriteLine( "int int" );
      }
    

This does not compile as the compiler does not know which of the four versions
it should call. As jules said _overloading is resolved at compile time by
looking at the compile time types. Multimethods are dispatched at run time, by
looking at the run time values._ In this case that would be the "int int"
version.

~~~
robododo
Thanks for the explanation.

That's cool and all, but why wouldn't I just define an interface with method
foo(), then override foo on my classes? This feels like class-based OO turned
inside out.

~~~
drblast
"At run time" is the key. Consider Java.

If Dog and Cat are subclasses of Animal (or implement an Animal interface),
for example, I might want to use a "breed" method to attempt to breed a hybrid
animal with the loyalty of a cat and the stealth of a dog.

    
    
      public Animal breed(Animal a, Animal b)
      {
      // return generic Animal
      }
    
      public Animal breed(Dog a, Cat b)
      {
      //return Cog
      }
    

But at compile time, if all I know is that I have two animals to breed, Java
will ALWAYS pick the breed() method with the Animal arguments, even if the
run-time types will be Dog and Cat.

To solve this problem in Java you have to delve into the hideous Java
reflection object and litter your code with junk that you'd wish the language
would figure out for you.

When you've used a language that is aware of types at run time and supports
multi-method dispatch, anything else seems like it's half-baked OO.

~~~
jstclair
And referring back to the original question on C#, you can do this using the
"dynamic" keyword -

[http://achoiusa.wordpress.com/2009/08/27/exploring-c-4-0-mul...](http://achoiusa.wordpress.com/2009/08/27/exploring-c-4-0-multimethods/)

------
SammyRulez
The power of a static typing in a dynamic typed language.. that's great.. and
elegant too! GO BDFL !

~~~
tikhonj
Except that you have to statically specify the types using decorators. So it's
more like a subset of static typing in a dynamically typed language.

~~~
skimbrel
It's not a subset of static typing at all.

If multimethods were a subset of static typing, then regular methods would be
too. Methods are dynamic by definition: the type of the method's target is
inspected at runtime and used to dispatch to the correct function entry. In
Python, it's explicitly stated because the first argument of all object
methods is "self". Multimethods extend this to do dynamic dispatch on all
argument types, not just the target object.

Most Python object methods are declared at compile time; this doesn't make
method declaration a "subset of static typing". You can add new methods to
classes and objects at runtime using Python's reflection and introspection;
you can just as easily add new multimethods at runtime using this framework.

Finally, the @decorator(...) syntax at compile time is just syntactic sugar
for something like the following:

    
    
      def _foo(self, arg):
        ...
      foo = decorator()(_foo)
    

So you can use decorators at runtime whenever you want too.

------
mhb
Not the main point but if the if-else _pattern gets tedious_ why is there no
switch in Python?

~~~
icebraining
Because a switch can usually be replaced by a dictionary?

    
    
        { 1: runOne, 2: runTwo, 3:runThree }[n]()
    

is equivalent to

    
    
        switch(n) { 
            case 1: runOne(); break; 
            case 2: runTwo(); break;
            case 3: runThree(); break;
        }

~~~
DanielRibeiro
Also, on Smalltalk (which influenced python) there is no switch statement as
well. On the other hand, Smalltalk has no if, while, for statements as well
(they are all methods).

~~~
gecko
While Smalltalk doesn't have case statements, a) it's trivial to add them, and
b) the way you get around it is totally different: rather than use a
Dictionary, you instead generally make a pile of tiny classes, each with a
fooBar: method, and then simply make sure you've got the right object type and
call fooBar: on it indiscriminately--in other words, basically your classic
visitor pattern.

That's totally possible in Python, too, but I don't really ever see it done.

~~~
datashaman
I like to use defaultdict for this. A strategy dictionary, with a default
strategy.

dispatch = defaultdict(default_stategy)

dispatch['one'] = new_strategy

print dispatch['two'] => default_strategy.

Beats case statements and elif chains.

~~~
zeekay
I usually just provide a default argument to get:

dispatch.get('two', default_strategy)

------
datashaman
This obfuscates code, and I remove it wherever I see it. Sometimes people are
too clever for their own good. Use an object. Override a method, FFS.

~~~
barrkel
Any trivial example demonstrating how something like this works will seem
obfuscatory, because examples are usually trivially solved with other
approaches. But see the Expression Problem[1] for the deep problem.

Whether it's an appropriate solution depends on whether introducing new
operations or new data types is more common; and where the modularity
boundaries are. For example, what if the base class in the hierarchy doesn't
have a method for the task you want to perform, and you can't add one because
it belongs to third party code, and nor can you implement it appropriately for
every descendant?

[1] <http://en.wikipedia.org/wiki/Expression_problem>

