Multiple dispatch

Multiple dispatch or multimethods or function overloading is the feature of some object-oriented programming languages in which a function or method can be dynamically dispatched based on the run time (dynamic) type of more than one of its arguments. This is an extension of single dispatch polymorphism where a method call is dynamically dispatched based on the actual derived type of the object on which the method has been called. Multiple dispatch generalizes the dynamic dispatching to work with a combination of two or more objects.

Contents

Understanding dispatch

Developers of computer software typically organize source code into named blocks variously called subroutines, procedures, subprograms, functions, or methods. The code in the function is executed by calling it - executing a piece of code that references its name. This transfers control temporarily to the called function; when the function's execution has completed, control is typically transferred back to the instruction in the caller that follows the reference.

Function names are usually selected so as to be descriptive of the function's purpose. It is sometimes desirable to give several functions the same name, often because they perform conceptually similar tasks, but operate on different types of input data. In such cases, the name reference at the function call site is not sufficient for identifying the block of code to be executed. Instead, the number and type of the arguments to the function call are also used to select among several function implementations.

In "conventional", i.e. single dispatch, object-oriented programming languages, when you invoke a method ("send a message" in Smalltalk, "call a member function" in C++) one of its arguments is treated specially and used to determine which of the (potentially many) methods of that name is to be applied. In many languages, the "special" argument is indicated syntactically; for example, a number of programming languages put the special argument before a dot in making a method call: special.method(other,arguments,here), so that lion.call() would produce a roar, whereas sparrow.call() would produce a cheep.

By contrast, in languages with multiple dispatch, the selected method is simply the one whose arguments match the number and type of the function call. There is no "special" argument that "owns" the function/method carried out in a particular call.

The Common Lisp Object System (CLOS) is an early and well-known example of multiple dispatch.

Data types

When working with languages that can discriminate data types at compile-time, selecting among the alternatives can occur at compile-time. The act of creating such alternative functions for compile-time selection is usually referred to as overloading a function.

In programming languages that defer data type identification until run-time, the selection among alternative functions must occur at run-time, based on the dynamically-determined types of function arguments. Functions whose alternative implementations are selected in this manner are referred to most generally as multimethods.

There is some run-time cost associated with dynamically dispatching function calls. In some languages[citation needed], the distinction between overloading and multimethods can be blurred, with the compiler determining whether compile-time selection can be applied to a given function call, or whether slower run-time dispatch is needed.

Examples

Distinguishing multiple and single dispatch may be made clearer by an example. Imagine a game which has, among its (user-visible) objects, spaceships and asteroids. When two objects collide, the program may need to do different things according to what has just hit what.

Multiple dispatch examples

Common Lisp

In a language with multiple dispatch, such as Common Lisp, it might look more like this:

 (defmethod collide-with ((x asteroid) (y asteroid))
   ;; deal with asteroid hitting asteroid
   )
 (defmethod collide-with ((x asteroid) (y spaceship))
   ;; deal with asteroid hitting spaceship
   )
 (defmethod collide-with ((x spaceship) (y asteroid))
   ;; deal with spaceship hitting asteroid
   )
 (defmethod collide-with ((x spaceship) (y spaceship))
   ;; deal with spaceship hitting spaceship
   )

and similarly for the other methods. Explicit testing and "dynamic casting" are not used.

In the presence of multiple dispatch, the traditional idea of methods as being defined in classes and contained in objects becomes less appealing—each collide-with method there is attached to two different classes, not one. Hence, the special syntax for method invocation generally disappears, so that method invocation looks exactly like ordinary function invocation, and methods are grouped not in classes but in generic functions.

Python

In languages that do not support multiple dispatch at the language definition or syntactic level, it is often possible to add multiple dispatch using a library extension. For example, the module multimethods.py provides CLOS-style multimethods for Python without changing the underlying syntax or keywords of the language.

from multimethods import Dispatch
from game_objects import Asteroid, Spaceship
from game_behaviors import ASFunc, SSFunc, SAFunc
collide = Dispatch()
collide.add_rule((Asteroid,  Spaceship), ASFunc)
collide.add_rule((Spaceship, Spaceship), SSFunc)
collide.add_rule((Spaceship,  Asteroid), SAFunc)
def AAFunc(a, b):
    """Behavior when asteroid hits asteroid"""
    # ...define new behavior...
collide.add_rule((Asteroid, Asteroid), AAFunc)
# ...later...
collide(thing1, thing2)

Functionally, this is very similar to the CLOS example, but the syntax is conventional Python.

Using Python 2.4 decorators, Guido van Rossum produced a sample implementation of multimethods [1] with a simplified syntax:

@multimethod(Asteroid, Asteroid)
def collide(a, b):
    """Behavior when asteroid hits asteroid"""
    # ...define new behavior...
@multimethod(Asteroid, Spaceship)
def collide(a, b):
    """Behavior when asteroid hits spaceship"""
    # ...define new behavior...
# ... define other multimethod rules ...

and then it goes on to define the multimethod decorator.

Python with PEAK-Rules
from peak.rules import abstract, when
 
@abstract 
def collide(a, b):
    "Process collision of two objects"
 
@when(collide, (Asteroid, Asteroid))
def collide_asteroids(asteroid1, asteroid2):
    pass
 
@when(collide, (Spaceship, Asteroid))
def collide_spaceship_with_asteroid(spaceship, asteroid):
    pass
 
@when(collide, (Asteroid, Spaceship))
def collide_asteroid_with_spaceship(asteroid, spaceship):
    return collide_spaceship_with_asteroid(spaceship, asteroid)
 
# etc...

Examples of emulating multiple dispatch

Java

In a language with only single dispatch, such as Java, the code would probably look something like this (although the visitor pattern can help to solve this problem):

 /* Example using run time type comparison via Java's "instanceof" operator */
 
 interface Collideable {
     /* making this a class would not change the demonstration */
     void collideWith(Collideable other);
 }
 
 class Asteroid implements Collideable {
     public void collideWith(Collideable other) {
         if (other instanceof Asteroid) {
             // handle Asteroid-Asteroid collision
         }
         else if (other instanceof Spaceship) {
             // handle Asteroid-Spaceship collision
         }
     }
 }
 
 class Spaceship implements Collideable {
     public void collideWith(Collideable other) {
         if (other instanceof Asteroid) {
             // handle Spaceship-Asteroid collision
         }
         else if (other instanceof Spaceship) {
             // handle Spaceship-Spaceship collision
         }
     }
 }

C

C does not have dynamic dispatch, so it must be implemented manually in some form. Often an enum is used to identify the subtype of an object. Dynamic dispatch can be done by looking up this value in a function pointer branch table. Here is a simple example in C:

typedef void (*CollisionCase)();
 
void collision_AA() { /* handle Asteroid-Asteroid collision*/   };
void collision_AS() { /* handle Asteroid-Spaceship collision*/  };
void collision_SA() { /* handle Spaceship-Asteroid collision*/  };
void collision_SS() { /* handle Spaceship-Spaceship collision*/ };
 
typedef enum {
    asteroid = 0,
    spaceship
} Thing;
 
enum {
    num_thing_types = 2
};
 
CollisionCase collisionCases[num_thing_types][num_thing_types] = {
    {&collision_AA, &collision_AS},
    {&collision_SA, &collision_SS} 
};
 
void collide(Thing a, Thing b) {
    (*collisionCases[a][b])();
}
 
int main() {
    collide(spaceship, asteroid);
}

C++

While adding them to C++ is being considered,[1] currently C++ only supports single dispatch natively. The methods of working around this limitation are analogous; either use the visitor pattern, double dispatch, dynamic cast:

 // Example using run time type comparison via dynamic_cast
 
 struct Thing {
     virtual void collideWith(Thing& other) = 0;
 }
 
 struct Asteroid : Thing {
     void collideWith(Thing& other) {
         // dynamic_cast to a pointer type returns NULL if the cast fails
         // (dynamic_cast to a reference type would throw an exception on failure)
         if (Asteroid* asteroid = dynamic_cast<Asteroid*>(&other)) {
             // handle Asteroid-Asteroid collision
         } else if (Spaceship* spaceship = dynamic_cast<Spaceship*>(&other)) {
             // handle Asteroid-Spaceship collision
         } else {
             // default collision handling here
         }
     }
 }
 
 struct Spaceship : Thing {
     void collideWith(Thing& other) {
         if (Asteroid* asteroid = dynamic_cast<Asteroid*>(&other)) {
             // handle Spaceship-Asteroid collision
         } else if (Spaceship* spaceship = dynamic_cast<Spaceship*>(&other)) {
             // handle Spaceship-Spaceship collision
         } else {
             // default collision handling here
         }
     }
 }

or pointer-to-method lookup table:

#include <typeinfo>
#include <hash_map>
 
typedef unsigned uint4;
typedef unsigned long long uint8;
 
class Thing {
  protected:
    Thing(const uint4 cid) : tid(cid) {
    }
    const uint4 tid; // type id
 
    typedef void (Thing::*CollisionHandler)( Thing& other);
    typedef std::hash_map<uint8, CollisionHandler> CollisionHandlerMap;
 
    static void addHandler(const uint4 id1, const uint4 id2, const CollisionHandler handler) {
        collisionCases.insert(CollisionHandlerMap::value_type(key(id1, id2), handler));
    }
    static uint8 key(const uint4 id1, const uint4 id2) { 
        return uint8(id1) << 32 | id2; 
    }
    static CollisionHandlerMap collisionCases;
 
  public:
    void collideWith(Thing& other) {
        CollisionHandlerMap::const_iterator handler = collisionCases.find(key(tid, other.tid));
        if (handler != collisionCases.end()) {
            (this->*handler->second)(other); // pointer-to-method call
        } else {
            // default collision handling
        }
    }
};
 
class Asteroid: public Thing {
    void asteroid_collision(Thing& other)   { /*handle Asteroid-Asteroid collision*/ }
    void spaceship_collision(Thing& other)  { /*handle Asteroid-Spaceship collision*/}
 
  public:
    Asteroid(): Thing(cid) {} 
    static void initCases();
    static const uint4 cid;
};
 
class Spaceship: public Thing {
    void asteroid_collision(Thing& other)   { /*handle Spaceship-Asteroid collision*/}
    void spaceship_collision(Thing& other)  { /*handle Spaceship-Spaceship collision*/}
 
  public:
    Spaceship(): Thing(cid) {
    } 
    static void initCases();
    static const uint4 cid; // class id
};
 
Thing::CollisionHandlerMap Thing::collisionCases;
const uint4 Asteroid::cid  = typeid(Asteroid).hash_code();
const uint4 Spaceship::cid = typeid(Spaceship).hash_code();
 
void Asteroid::initCases() {
    addHandler(cid, cid, (CollisionHandler) &Asteroid::asteroid_collision);
    addHandler(cid, Spaceship::cid, (CollisionHandler) &Asteroid::spaceship_collision);
}
 
void Spaceship::initCases() {
    addHandler(cid, Asteroid::cid, (CollisionHandler) &Spaceship::asteroid_collision);
    addHandler(cid, cid, (CollisionHandler) &Spaceship::spaceship_collision);
}
 
int main() {
    Asteroid::initCases();
    Spaceship::initCases();
 
    Asteroid  a1, a2;
    Spaceship s1, s2;
 
    a1.collideWith(a2);
    a1.collideWith(s1);
 
    s1.collideWith(s2);
    s1.collideWith(a1);
}


Stroustrup mentions in The Design and Evolution of C++ that he liked the concept of Multi-methods and considered implementing it in C++ but claims to have been unable to find an efficient sample implementation (comparable to virtual functions) and resolve some possible type ambiguity problems. He goes on to state that although the feature would still be nice to have, that it can be approximately implemented using double dispatch or a type based lookup table as outlined in the C/C++ example above so is a low priority feature for future language revisions.[2]

Support in programming languages

Programming languages that support general multimethods:

Multimethods in other programming languages via extensions:

References

  1. ^ http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2216.pdf
  2. ^ Stroustrup, Bjarne (1994). "Section 13.8". The Design and Evolution of C++. Indianapolis, IN, U.S.A: Addison Wesley. ISBN 0201543303. 
  3. ^ Steele, Guy L. (1990). "chapter 28". Common LISP: The Language. Bedford, MA, U.S.A: Digital Press. ISBN 1555580416. http://books.google.com/books?id=8Hr3ljbCtoAC. 
  4. ^ "Type classes: exploring the design space". http://research.microsoft.com/en-us/um/people/simonpj/Papers/type-class-design-space/. Retrieved 1997-05-02. 
  5. ^ "Background and Goals". http://www.opendylan.org/books/drm/Background_and_Goals. Retrieved 2008-04-13. 
  6. ^ "Visitor Pattern Versus Multimethods". http://nice.sourceforge.net/visitor.html. Retrieved 2008-04-13. 
  7. ^ "Cecil Language". http://www.cs.washington.edu/research/projects/cecil/www/cecil.html. Retrieved 2008-04-13. 
  8. ^ "How S4 Methods Work". http://developer.r-project.org/howMethodsWork.pdf. Retrieved 2008-04-13. 
  9. ^ "Multimethods in Groovy". http://blogs.sun.com/sundararajan/entry/multimethods_in_groovy. Retrieved 2008-04-13. 
  10. ^ "Perl 6 FAQ". http://dev.perl.org/perl6/faq.html. Retrieved 2008-04-13. 
  11. ^ "Multiple Dispatch in Seed7". http://seed7.sourceforge.net/manual/objects.htm#multiple_dispatch. Retrieved 2011-04-23. 
  12. ^ "Multimethods in Clojure". http://clojure.org/multimethods. Retrieved 2008-09-04. 
  13. ^ "Multimethods in C# 4.0 With 'Dynamic'". http://blogs.msdn.com/laurionb/archive/2009/08/13/multimethods-in-c-4-0-with-dynamic.aspx. Retrieved 2009-08-20. 
  14. ^ "The Fortress Language Specification, Version 1.0". http://research.sun.com/projects/plrg/Publications/fortress.1.0.pdf. Retrieved 2010-04-23. 

External links

  • Research paper on how to cleanly add multiple dispatch to C++ by Bjarne Stroustrup, Yuriy Solodkyy and Peter Pirkelbauer

Wikimedia Foundation. 2010.

Look at other dictionaries:

  • Multiple dispatch — Dispatch multiple Le dispatch multiple est une fonctionnalité de certains langages orientés objet ou langages fonctionnels dans lesquels une fonction ou une méthode peut être spécialisée pour plus d un de ses paramètres formels. On l appelle… …   Wikipédia en Français

  • Multiple — Cette page d’homonymie répertorie les différents sujets et articles partageant un même nom. Sur les autres projets Wikimedia : « Multiple », sur le Wiktionnaire (dictionnaire universel) Le mot multiple peut être employé comme  …   Wikipédia en Français

  • Dispatch Multiple — Le dispatch multiple est une fonctionnalité de certains langages orientés objet ou langages fonctionnels dans lesquels une fonction ou une méthode peut être spécialisée pour plus d un de ses paramètres formels. On l appelle alors multiméthode.… …   Wikipédia en Français

  • Dispatch (band) — Dispatch Origin Middlebury, Vermont, United States Genres Jam band, indie folk, roots rock, reggae fusion, rap rock, ska Years active 1996–2002, 2004, 2007, 2009, 2011–pr …   Wikipedia

  • Dispatch (logistics) — Dispatch is a procedure for assigning employees (workers) or vehicles to customers. Industries that dispatch include taxicabs, couriers, emergency services, as well as home and commercial services such as maid services, plumbing, HVAC, pest… …   Wikipedia

  • Multiple inheritance — is a feature of some object oriented computer programming languages in which a class can inherit behaviors and features from more than one superclass. Languages that support multiple inheritance include: C++, Common Lisp (via CLOS), EuLisp (via… …   Wikipedia

  • Multiple-alarm fire — One alarm, two alarm, three alarm fires, or higher, are categories of fires indicating the level of response by local authorities, with an elevated number of alarms indicating increased commitment of resources. The term multiple alarm is a quick… …   Wikipedia

  • Dispatch multiple — Le dispatch multiple est une fonctionnalité de certains langages orientés objet ou langages fonctionnels dans lesquels une fonction ou une méthode peut être spécialisée pour plus d un de ses paramètres formels. On l appelle alors multiméthode.… …   Wikipédia en Français

  • Multiple Rocket Launcher System — MRLS, system that can dispatch several rockets …   English contemporary dictionary

  • Dynamic dispatch — Theories and practice of polymorphism Double dispatch Multiple dispatch Operator overloading Polymorphism in computer science Polymorphism in OOP Subtyping Vir …   Wikipedia

Share the article and excerpts

Direct link
Do a right-click on the link above
and select “Copy Link”