Supriya Ghosh (Editor)

Multiple dispatch

Updated on
Edit
Like
Comment
Share on FacebookTweet on TwitterShare on LinkedInShare on Reddit

Multiple dispatch or multimethods is a feature of some programming languages in which a function or method can be dynamically dispatched based on the run-time (dynamic) type or, in the more general case some other attribute, of more than one of its arguments. This is a generalization of single-dispatch polymorphism where a function or method call is dynamically dispatched based on the actual derived type of the object on which the method has been called. Multiple dispatch routes the dynamic dispatch to the implementing function or method using the combined characteristics of one or more arguments.

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 more conventional, i.e. single-dispatch object-oriented programming languages, when invoking a method (sending a message in Smalltalk, calling 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.sound() would produce a roar, whereas sparrow.sound() 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 (i.e., late binding), 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, 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.

Use in practice

In order to estimate how often multiple dispatch is used in practice, Muschevici et al. studied programs that use dynamic dispatch. They analyzed nine applications, mostly compilers, written in six different languages: Common Lisp Object System, Dylan, Cecil, MultiJava, Diesel, and Nice. Their results show that 13–32% of generic functions use the dynamic type of a one argument, while 2.7–6.5% of them use the dynamic type of multiple arguments. The remaining 65–93% of generic functions have one concrete method (overrider), and thus are not considered to use the dynamic types of their arguments. Further, the study reports that 2–20% of generic functions had two and 3–6% had three concrete function implementations. The numbers decrease rapidly for functions with more concrete overriders.

Theory

The theory of multiple dispatching languages was first developed by Castagna et al., by defining a model for overloaded functions with late binding. It yielded the first formalization of the problem of covariance and contravariance of object oriented languages and a solution to the problem of binary methods.

Examples

Distinguishing multiple and single dispatch may be made clearer by an example. Imagine a game that 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.

Common Lisp

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

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.

Perl 6

Perl 6, like past versions, uses proven ideas from other languages, and type systems have shown themselves to offer compelling advantages in compiler-side code analysis and powerful user-side semantics via multiple dispatch.

It has both multi methods, and multi subs. Since most operators are actually subroutines, it has multiple dispatched operators as well.

Along with the usual type constraints, it also has "where" constraints that allow you to make very specialized subroutines.

Python

In languages that do not support multiple dispatch at the language definition or syntactic level, such as Python, 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.

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 with a simplified syntax:

and then it goes on to define the multimethod decorator.

The PEAK-Rules package provides multiple dispatch with a syntax similar to the above example.

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:

With the C Object System library, C does support dynamic dispatch similar to CLOS. It is fully extensible and does not need any manual handling of the methods. Dynamic message (methods) are dispatched by the dispatcher of COS, which is faster than Objective-C. Here is an example in COS:

C++

As of 2015, C++ natively supports only single dispatch, though adding multi-dispatch is being considered. The methods of working around this limitation are analogous: use either the visitor pattern or dynamic cast:

or pointer-to-method lookup table:

The yomm11 library automates this approach.

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 then states 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.

Java

In a language with only single dispatch, such as Java, multiple dispatch can be emulated with multiple levels of single dispatch:

Run time instanceof checks at one or both levels can also be used.

Support in programming languages

Programming languages that support general multimethods:

  • C# 4.0
  • Cecil
  • Clojure
  • Common Lisp (via the Common Lisp Object System)
  • Dylan
  • Elixir
  • Fortress
  • Groovy
  • Haskell via multi-parameter type classes
  • Julia
  • Lasso
  • Nice
  • Nim
  • Perl 6
  • R
  • Scala, also via multi-parameter type classes
  • Seed7
  • TADS
  • Wolfram Language via symbolic pattern matching
  • Xtend
  • Multimethods in other programming languages via extensions:

  • .NET (via the library MultiMethods.NET)
  • C (via the library C Object System)
  • C# (via the library multimethod-sharp)
  • C++ (via the library yomm11)
  • Factor (via the standard multi-methods vocabulary)
  • Java (using the extension MultiJava)
  • Perl (via the module Class::Multimethods)
  • Python (via PEAK-Rules, RuleDispatch, gnosis.magic.multimethods, PyMultimethods, or multipledispatch)
  • Ruby (via the library The Multiple Dispatch Library and Multimethod Package and Vlx-Multimethods Package)
  • Scheme (via e.g. TinyCLOS)
  • Also, multi-parameter type classes in Haskell and Scala can be used to emulate multiple dispatch.

    References

    Multiple dispatch Wikipedia