Open main menu
Home
Random
Recent changes
Special pages
Community portal
Preferences
About Wikipedia
Disclaimers
Incubator escapee wiki
Search
User menu
Talk
Dark mode
Contributions
Create account
Log in
Editing
Multiple dispatch
(section)
Warning:
You are not logged in. Your IP address will be publicly visible if you make any edits. If you
log in
or
create an account
, your edits will be attributed to your username, along with other benefits.
Anti-spam check. Do
not
fill this in!
== 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. === Languages with built-in multiple dispatch === ==== C# ==== [[C Sharp (programming language)|C#]] introduced support for dynamic multimethods in version 4<ref>{{cite web |url=https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/types/using-type-dynamic |title=Using type dynamic (C# Programming Guide) |access-date=2020-05-14 }}</ref> (April 2010) using the 'dynamic' keyword. The following example demonstrates multimethods. Like many other statically-typed languages, C# also supports static method overloading.<ref>{{cite web |url=https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/basic-concepts#signatures-and-overloading |title=Basic concepts |access-date=2020-05-14 }}</ref> Microsoft expects that developers will choose static typing over dynamic typing in most scenarios.<ref>{{cite web |url=https://docs.microsoft.com/en-us/archive/msdn-magazine/2011/february/msdn-magazine-dynamic-net-understanding-the-dynamic-keyword-in-csharp-4 |title=Dynamic .NET - Understanding the Dynamic Keyword in C# 4 |date=10 August 2015 |access-date=2020-05-14 }}</ref> The 'dynamic' keyword supports interoperability with COM objects and dynamically-typed .NET languages. [[File:Csharp ColliderLibrary.svg]] The example below uses features introduced in C# 9 and C# 10. <syntaxhighlight lang="c#"> using static ColliderLibrary; Console.WriteLine(Collide(new Asteroid(101), new Spaceship(300))); Console.WriteLine(Collide(new Asteroid(10), new Spaceship(10))); Console.WriteLine(Collide(new Spaceship(101), new Spaceship(10))); string Collide(SpaceObject x, SpaceObject y) => x.Size > 100 && y.Size > 100 ? "Big boom!" : CollideWith(x as dynamic, y as dynamic); // Dynamic dispatch to CollideWith method class ColliderLibrary { public static string CollideWith(Asteroid x, Asteroid y) => "a/a"; public static string CollideWith(Asteroid x, Spaceship y) => "a/s"; public static string CollideWith(Spaceship x, Asteroid y) => "s/a"; public static string CollideWith(Spaceship x, Spaceship y) => "s/s"; } abstract record SpaceObject(int Size); record Asteroid(int Size) : SpaceObject(Size); record Spaceship(int Size) : SpaceObject(Size); </syntaxhighlight> Output: <syntaxhighlight lang="output"> Big boom! a/s s/s </syntaxhighlight> ==== Groovy ==== [[Apache Groovy|Groovy]] is a general purpose [[Java (programming language)|Java]] compatible/interusable [[Java virtual machine|JVM]] language, which, contrary to Java, uses late binding / multiple dispatch.<ref>[https://groovy-lang.org/differences.html#_multi_methods Groovy - Multi-methods]</ref> <syntaxhighlight lang="groovy"> /* Groovy implementation of C# example above Late binding works the same when using non-static methods or compiling class/methods statically (@CompileStatic annotation) */ class Program { static void main(String[] args) { println Collider.collide(new Asteroid(101), new Spaceship(300)) println Collider.collide(new Asteroid(10), new Spaceship(10)) println Collider.collide(new Spaceship(101), new Spaceship(10)) } } class Collider { static String collide(SpaceObject x, SpaceObject y) { (x.size > 100 && y.size > 100) ? "big-boom" : collideWith(x, y) // Dynamic dispatch to collideWith method } private static String collideWith(Asteroid x, Asteroid y) { "a/a" } private static String collideWith(Asteroid x, Spaceship y) { "a/s" } private static String collideWith(Spaceship x, Asteroid y) { "s/a" } private static String collideWith(Spaceship x, Spaceship y) { "s/s"} } class SpaceObject { int size SpaceObject(int size) { this.size = size } } @InheritConstructors class Asteroid extends SpaceObject {} @InheritConstructors class Spaceship extends SpaceObject {} </syntaxhighlight> ==== Common Lisp ==== In a language with multiple dispatch, such as [[Common Lisp]], it might look more like this (Common Lisp example shown): <syntaxhighlight lang="lisp"> (defclass asteroid () ((size :reader size :initarg :size))) (defclass spaceship () ((size :reader size :initarg :size))) (defun space-object (class size) (make-instance class :size size)) ; collide-with is a generic function with multiple dispatch (defmethod collide-with ((x asteroid) (y asteroid)) "a/a") (defmethod collide-with ((x asteroid) (y spaceship)) "a/s") (defmethod collide-with ((x spaceship) (y asteroid)) "s/a") (defmethod collide-with ((x spaceship) (y spaceship)) "s/s") (defun collide (x y) (if (and (> (size x) 100) (> (size y) 100)) "big-boom" (collide-with x y))) (print (collide (space-object 'asteroid 101) (space-object 'spaceship 300))) (print (collide (space-object 'asteroid 10) (space-object 'spaceship 10))) (print (collide (space-object 'spaceship 101) (space-object 'spaceship 10))) </syntaxhighlight> 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 above 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 function]]s. ==== Julia ==== [[Julia (programming language)|Julia]] has built-in multiple dispatch, and it is central to the language design.<ref name=julia-review>{{cite journal |last1=Bezanson |first1=Jeff |last2=Edelman |first2=Alan |last3=Karpinski |first3=Stefan |last4=Shah |first4=Viral B. |title=Julia: A fresh approach to numerical computing |journal=SIAM Review |volume=59 |issue=1 |pages=65–98 |date=7 February 2017 |doi=10.1137/141000671 |arxiv=1411.1607|s2cid=13026838 }}</ref> The Julia version of the example above might look like: <syntaxhighlight lang="julia"> abstract type SpaceObject end struct Asteroid <: SpaceObject size::Int end struct Spaceship <: SpaceObject size::Int end collide_with(::Asteroid, ::Spaceship) = "a/s" collide_with(::Spaceship, ::Asteroid) = "s/a" collide_with(::Spaceship, ::Spaceship) = "s/s" collide_with(::Asteroid, ::Asteroid) = "a/a" collide(x::SpaceObject, y::SpaceObject) = (x.size > 100 && y.size > 100) ? "Big boom!" : collide_with(x, y) </syntaxhighlight> Output: <syntaxhighlight lang="julia-repl"> julia> collide(Asteroid(101), Spaceship(300)) "Big boom!" julia> collide(Asteroid(10), Spaceship(10)) "a/s" julia> collide(Spaceship(101), Spaceship(10)) "s/s" </syntaxhighlight> ==== Raku ==== [[Raku (programming language)|Raku]], like Perl, 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 multimethods, and multisubs. Since most operators are subroutines, it also has multiple dispatched operators. Along with the usual type constraints, it also has ''where'' constraints that allow making very specialized subroutines. <syntaxhighlight lang="raku"> subset Mass of Real where 0 ^..^ Inf; role Stellar-Object { has Mass $.mass is required; method name () returns Str {...}; } class Asteroid does Stellar-Object { method name () { 'an asteroid' } } class Spaceship does Stellar-Object { has Str $.name = 'some unnamed spaceship'; } my Str @destroyed = < obliterated destroyed mangled >; my Str @damaged = « damaged 'collided with' 'was damaged by' »; # We add multi candidates to the numeric comparison operators because we are comparing them numerically, # but makes no sense to have the objects coerce to a Numeric type. # ( If they did coerce we wouldn't necessarily need to add these operators. ) # We could have also defined entirely new operators this same way. multi sub infix:« <=> » ( Stellar-Object:D $a, Stellar-Object:D $b ) { $a.mass <=> $b.mass } multi sub infix:« < » ( Stellar-Object:D $a, Stellar-Object:D $b ) { $a.mass < $b.mass } multi sub infix:« > » ( Stellar-Object:D $a, Stellar-Object:D $b ) { $a.mass > $b.mass } multi sub infix:« == » ( Stellar-Object:D $a, Stellar-Object:D $b ) { $a.mass == $b.mass } # Define a new multi dispatcher, and add some type constraints to the parameters. # If we didn't define it we would have gotten a generic one that didn't have constraints. proto sub collide ( Stellar-Object:D $, Stellar-Object:D $ ) {*} # No need to repeat the types here since they are the same as the prototype. # The 'where' constraint technically only applies to $b not the whole signature. # Note that the 'where' constraint uses the `<` operator candidate we added earlier. multi sub collide ( $a, $b where $a < $b ) { say "$a.name() was @destroyed.pick() by $b.name()"; } multi sub collide ( $a, $b where $a > $b ) { # redispatch to the previous candidate with the arguments swapped samewith $b, $a; } # This has to be after the first two because the other ones # have 'where' constraints, which get checked in the # order the subs were written. ( This one would always match. ) multi sub collide ( $a, $b ) { # randomize the order my ($n1, $n2) = ( $a.name, $b.name ).pick(*); say "$n1 @damaged.pick() $n2"; } # The following two candidates can be anywhere after the proto, # because they have more specialized types than the preceding three. # If the ships have unequal mass one of the first two candidates gets called instead. multi sub collide ( Spaceship $a, Spaceship $b where $a == $b ){ my ($n1, $n2) = ( $a.name, $b.name ).pick(*); say "$n1 collided with $n2, and both ships were ", ( @destroyed.pick, 'left damaged' ).pick; } # You can unpack the attributes into variables within the signature. # You could even have a constraint on them `(:mass($a) where 10)`. multi sub collide ( Asteroid $ (:mass($a)), Asteroid $ (:mass($b)) ){ say "two asteroids collided and combined into one larger asteroid of mass { $a + $b }"; } my Spaceship $Enterprise .= new(:mass(1),:name('The Enterprise')); collide Asteroid.new(:mass(.1)), $Enterprise; collide $Enterprise, Spaceship.new(:mass(.1)); collide $Enterprise, Asteroid.new(:mass(1)); collide $Enterprise, Spaceship.new(:mass(1)); collide Asteroid.new(:mass(10)), Asteroid.new(:mass(5)); </syntaxhighlight> === Extending languages with multiple-dispatch libraries === ==== JavaScript ==== 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 (computing)|library]] extension. JavaScript and TypeScript do not support multimethods at the syntax level, but it is possible to add multiple dispatch via a library. For example, the ''multimethod package''<ref name="multimethod_package">[https://www.npmjs.com/package/@arrows/multimethod @arrows/multimethod] Multiple dispatch in JavaScript/TypeScript with configurable dispatch resolution by Maciej Cąderek.</ref> provides an implementation of multiple dispatch, generic functions. Dynamically-typed version in JavaScript: <syntaxhighlight lang="javascript"> import { multi, method } from '@arrows/multimethod' class Asteroid {} class Spaceship {} const collideWith = multi( method([Asteroid, Asteroid], (x, y) => { // deal with asteroid hitting asteroid }), method([Asteroid, Spaceship], (x, y) => { // deal with asteroid hitting spaceship }), method([Spaceship, Asteroid], (x, y) => { // deal with spaceship hitting asteroid }), method([Spaceship, Spaceship], (x, y) => { // deal with spaceship hitting spaceship }), ) </syntaxhighlight> Statically-typed version in TypeScript: <syntaxhighlight lang="typescript"> import { multi, method, Multi } from '@arrows/multimethod' class Asteroid {} class Spaceship {} type CollideWith = Multi & { (x: Asteroid, y: Asteroid): void (x: Asteroid, y: Spaceship): void (x: Spaceship, y: Asteroid): void (x: Spaceship, y: Spaceship): void } const collideWith: CollideWith = multi( method([Asteroid, Asteroid], (x, y) => { // deal with asteroid hitting asteroid }), method([Asteroid, Spaceship], (x, y) => { // deal with asteroid hitting spaceship }), method([Spaceship, Asteroid], (x, y) => { // deal with spaceship hitting asteroid }), method([Spaceship, Spaceship], (x, y) => { // deal with spaceship hitting spaceship }), ) </syntaxhighlight> ==== Python ==== Multiple dispatch can be added to [[Python (programming language)|Python]] using a [[Library (computing)|library]] extension. For example, using the module ''multimethod.py''<ref>{{Citation|last=Coady|first=Aric|title=multimethod: Multiple argument dispatching.|url=https://github.com/coady/multimethod|access-date=2021-01-28}}</ref> and also with the module ''multimethods.py''<ref name="multimethods_module">[http://gnosis.cx/download/gnosis/magic/multimethods.py multimethods.py] {{Webarchive|url=https://web.archive.org/web/20050309230813/http://gnosis.cx/download/gnosis/magic/multimethods.py |date=2005-03-09 }}, Multiple dispatch in Python with configurable dispatch resolution by David Mertz, et al.</ref> which provides CLOS-style multimethods for [[Python (programming language)|Python]] without changing the underlying syntax or keywords of the language. <syntaxhighlight lang="python"> from multimethods import Dispatch from game_objects import Asteroid, Spaceship from game_behaviors import as_func, ss_func, sa_func collide = Dispatch() collide.add_rule((Asteroid, Spaceship), as_func) collide.add_rule((Spaceship, Spaceship), ss_func) collide.add_rule((Spaceship, Asteroid), sa_func) def aa_func(a, b): """Behavior when asteroid hits asteroid.""" # ...define new behavior... collide.add_rule((Asteroid, Asteroid), aa_func) </syntaxhighlight> <syntaxhighlight lang="python"> # ...later... collide(thing1, thing2) </syntaxhighlight> Functionally, this is very similar to the CLOS example, but the syntax is conventional Python. Using Python 2.4 [[Python syntax and semantics#Decorators|decorators]], [[Guido van Rossum]] produced a sample implementation of multimethods<ref>{{Cite web | url=http://www.artima.com/weblogs/viewpost.jsp?thread=101605 |title=Five-minute Multimethods in Python}}</ref> with a simplified syntax: <syntaxhighlight lang="python"> @multimethod(Asteroid, Asteroid) def collide(a, b): """Behavior when asteroid hits a asteroid.""" # ...define new behavior... @multimethod(Asteroid, Spaceship) def collide(a, b): """Behavior when asteroid hits a spaceship.""" # ...define new behavior... # ... define other multimethod rules ... </syntaxhighlight> 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.<ref>{{cite web |title=PEAK-Rules 0.5a1.dev |url=https://pypi.python.org/pypi/PEAK-Rules |website = Python Package Index |access-date=21 March 2014}}</ref> It was later replaced by PyProtocols.<ref>{{cite web |title=PyProtocols |url=http://peak.telecommunity.com/protocol_ref/module-protocols.html |website = Python Enterprise Application Kit |access-date=26 April 2019}}</ref> The Reg library also supports multiple and predicate dispatch.<ref>{{cite web |title=Reg |url=https://reg.readthedocs.io/en/latest/ |website = Read the docs |access-date=26 April 2019}}</ref> With the introduction of [[Python syntax and semantics#Function annotations|type hints]], multiple dispatch is possible with even simpler syntax. For example, using [https://github.com/beartype/plum plum-dispatch],<syntaxhighlight lang="python"> from plum import dispatch @dispatch def collide(a: Asteroid, b: Asteroid): """Behavior when asteroid hits a asteroid.""" # ...define new behavior... @dispatch def collide(a: Asteroid, b: Spaceship): """Behavior when asteroid hits a spaceship.""" # ...define new behavior... # ...define further rules... </syntaxhighlight> === Emulating multiple dispatch === ==== 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: <syntaxhighlight lang="c"> typedef void (*CollisionCase)(void); void collision_AA(void) { /* handle Asteroid-Asteroid collision */ }; void collision_AS(void) { /* handle Asteroid-Spaceship collision */ }; void collision_SA(void) { /* handle Spaceship-Asteroid collision */ }; void collision_SS(void) { /* handle Spaceship-Spaceship collision*/ }; typedef enum { THING_ASTEROID = 0, THING_SPACESHIP, THING_COUNT /* not a type of thing itself, instead used to find number of things */ } Thing; CollisionCase collisionCases[THING_COUNT][THING_COUNT] = { {&collision_AA, &collision_AS}, {&collision_SA, &collision_SS} }; void collide(Thing a, Thing b) { (*collisionCases[a][b])(); } int main(void) { collide(THING_SPACESHIP, THING_ASTEROID); } </syntaxhighlight> With the C Object System library,<ref>{{Cite web | url=https://github.com/CObjectSystem/COS |title=C Object System: A framework that brings C to the level of other high level programming languages and beyond: CObjectSystem/COS |website=[[GitHub]] |date=2019-02-19}}</ref> 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: <syntaxhighlight lang="c"> #include <stdio.h> #include <cos/Object.h> #include <cos/gen/object.h> // classes defclass (Asteroid) // data members endclass defclass (Spaceship) // data members endclass // generics defgeneric (_Bool, collide_with, _1, _2); // multimethods defmethod (_Bool, collide_with, Asteroid, Asteroid) // deal with asteroid hitting asteroid endmethod defmethod (_Bool, collide_with, Asteroid, Spaceship) // deal with asteroid hitting spaceship endmethod defmethod (_Bool, collide_with, Spaceship, Asteroid) // deal with spaceship hitting asteroid endmethod defmethod (_Bool, collide_with, Spaceship, Spaceship) // deal with spaceship hitting spaceship endmethod // example of use int main(void) { OBJ a = gnew(Asteroid); OBJ s = gnew(Spaceship); printf("<a,a> = %d\n", collide_with(a, a)); printf("<a,s> = %d\n", collide_with(a, s)); printf("<s,a> = %d\n", collide_with(s, a)); printf("<s,s> = %d\n", collide_with(s, s)); grelease(a); grelease(s); } </syntaxhighlight> ==== C++ ==== {{As of|2021}}, [[C++]] natively supports only single dispatch, though adding multi-methods (multiple dispatch) was proposed by [[Bjarne Stroustrup]] (and collaborators) in 2007.<ref>{{Cite web|last=|first=|date=2007-03-11|title=Report on language support for Multi-Methods and Open-Methods for C ++|url=http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2216.pdf|quote=Multiple dispatch – the selection of a function to be invoked based on the dynamic type of two or more arguments – is a solution to several classical problems in object-oriented programming.|archive-url=|archive-date=|access-date=|website=}}</ref> The methods of working around this limit are analogous: use either the [[visitor pattern]], dynamic cast or a library: <syntaxhighlight lang="cpp"> // 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 (auto asteroid = dynamic_cast<Asteroid*>(&other)) { // handle Asteroid-Asteroid collision } else if (auto spaceship = dynamic_cast<Spaceship*>(&other)) { // handle Asteroid-Spaceship collision } else { // default collision handling here } } }; struct Spaceship : Thing { void collideWith(Thing& other) { if (auto asteroid = dynamic_cast<Asteroid*>(&other)) { // handle Spaceship-Asteroid collision } else if (auto spaceship = dynamic_cast<Spaceship*>(&other)) { // handle Spaceship-Spaceship collision } else { // default collision handling here } } }; </syntaxhighlight> or pointer-to-method lookup table: <syntaxhighlight lang="cpp"> #include <cstdint> #include <typeinfo> #include <unordered_map> class Thing { protected: Thing(std::uint32_t cid) : tid(cid) {} const std::uint32_t tid; // type id typedef void (Thing::*CollisionHandler)(Thing& other); typedef std::unordered_map<std::uint64_t, CollisionHandler> CollisionHandlerMap; static void addHandler(std::uint32_t id1, std::uint32_t id2, CollisionHandler handler) { collisionCases.insert(CollisionHandlerMap::value_type(key(id1, id2), handler)); } static std::uint64_t key(std::uint32_t id1, std::uint32_t id2) { return std::uint64_t(id1) << 32 | id2; } static CollisionHandlerMap collisionCases; public: void collideWith(Thing& other) { auto 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 std::uint32_t 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 std::uint32_t cid; // class id }; Thing::CollisionHandlerMap Thing::collisionCases; const std::uint32_t Asteroid::cid = typeid(Asteroid).hash_code(); const std::uint32_t 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); } </syntaxhighlight> The ''YOMM2'' library<ref name="yomm2">[https://github.com/jll63/yomm2 yomm2], Fast, Orthogonal Open Multi-Methods for C++ by Jean-Louis Leroy.</ref> provides a fast, orthogonal implementation of open multimethods. The syntax for declaring open methods is inspired by a proposal for a native C++ implementation. The library requires that the user registers all the classes used as virtual arguments (and their sub-classes), but does not require any modifications to existing code. Methods are implemented as ordinary inline C++ functions; they can be overloaded and they can be passed by pointer. There is no limit on the number of virtual arguments, and they can be arbitrarily mixed with non-virtual arguments. The library uses a combination of techniques (compressed dispatch tables, collision free integer hash table) to implement method calls in constant time, while mitigating memory usage. Dispatching a call to an open method with a single virtual argument takes only 15–30% more time than calling an ordinary virtual member function, when a modern optimizing compiler is used. The Asteroids example can be implemented as follows: <syntaxhighlight lang="cpp"> #include <yorel/yomm2/keywords.hpp> #include <memory> class Thing { public: virtual ~Thing() {} }; class Asteroid : public Thing { }; class Spaceship : public Thing { }; register_classes(Thing, Spaceship, Asteroid); declare_method(void, collideWith, (virtual_<Thing&>, virtual_<Thing&>)); define_method(void, collideWith, (Thing& left, Thing& right)) { // default collision handling } define_method(void, collideWith, (Asteroid& left, Asteroid& right)) { // handle Asteroid-Asteroid collision } define_method(void, collideWith, (Asteroid& left, Spaceship& right)) { // handle Asteroid-Spaceship collision } define_method(void, collideWith, (Spaceship& left, Asteroid& right)) { // handle Spaceship-Asteroid collision } define_method(void, collideWith, (Spaceship& left, Spaceship& right)) { // handle Spaceship-Spaceship collision } int main() { yorel::yomm2::update_methods(); std::unique_ptr<Thing> a1(std::make_unique<Asteroid>()), a2(std::make_unique<Asteroid>()); std::unique_ptr<Thing> s1(std::make_unique<Spaceship>()), s2(std::make_unique<Spaceship>()); // note: types partially erased collideWith(*a1, *a2); // Asteroid-Asteroid collision collideWith(*a1, *s1); // Asteroid-Spaceship collision collideWith(*s1, *a1); // Spaceship-Asteroid collision collideWith(*s1, *s2); // Spaceship-Spaceship collision return 0; } </syntaxhighlight> Stroustrup mentions in ''The Design and Evolution of C++'' that he liked the concept of multimethods 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.<ref>{{cite book |last=Stroustrup |first=Bjarne |title=The Design and Evolution of C++ |publisher=Addison Wesley |location=Indianapolis, IN, U.S.A |year=1994 |chapter=Section 13.8 |isbn=978-0-201-54330-8|bibcode=1994dec..book.....S }}</ref> ==== D ==== {{As of|2021}}, as do many other object-oriented programming languages, [[D (programming language)|D]] natively supports only single dispatch. However, it is possible to emulate open multimethods as a library function in D. The ''openmethods'' library<ref name="openmethods">[https://github.com/jll63/methods.d openmethods], Open Multi-Methods for D by Jean-Louis Leroy.</ref> is an example. <syntaxhighlight lang="d"> // Declaration Matrix plus(virtual!Matrix, virtual!Matrix); // The override for two DenseMatrix objects @method Matrix _plus(DenseMatrix a, DenseMatrix b) { const int nr = a.rows; const int nc = a.cols; assert(a.nr == b.nr); assert(a.nc == b.nc); auto result = new DenseMatrix; result.nr = nr; result.nc = nc; result.elems.length = a.elems.length; result.elems[] = a.elems[] + b.elems[]; return result; } // The override for two DiagonalMatrix objects @method Matrix _plus(DiagonalMatrix a, DiagonalMatrix b) { assert(a.rows == b.rows); double[] sum; sum.length = a.elems.length; sum[] = a.elems[] + b.elems[]; return new DiagonalMatrix(sum); } </syntaxhighlight> ==== Java ==== In a language with only single dispatch, such as [[Java (programming language)|Java]], multiple dispatch can be emulated with multiple levels of single dispatch: [[File:UML class Java single dispatch.svg|UML class Java single dispatch.svg]] <syntaxhighlight lang="java"> interface Collideable { void collideWith(final Collideable other); /* These methods would need different names in a language without method overloading. */ void collideWith(final Asteroid asteroid); void collideWith(final Spaceship spaceship); } class Asteroid implements Collideable { public void collideWith(final Collideable other) { // Call collideWith on the other object. other.collideWith(this); } public void collideWith(final Asteroid asteroid) { // Handle Asteroid-Asteroid collision. } public void collideWith(final Spaceship spaceship) { // Handle Asteroid-Spaceship collision. } } class Spaceship implements Collideable { public void collideWith(final Collideable other) { // Call collideWith on the other object. other.collideWith(this); } public void collideWith(final Asteroid asteroid) { // Handle Spaceship-Asteroid collision. } public void collideWith(final Spaceship spaceship) { // Handle Spaceship-Spaceship collision. } } </syntaxhighlight> Run time <code>instanceof</code> checks at one or both levels can also be used.
Edit summary
(Briefly describe your changes)
By publishing changes, you agree to the
Terms of Use
, and you irrevocably agree to release your contribution under the
CC BY-SA 4.0 License
and the
GFDL
. You agree that a hyperlink or URL is sufficient attribution under the Creative Commons license.
Cancel
Editing help
(opens in new window)