Double Dispatch

From Plasmaworks

Jump to: navigation, search

Contents

Single Dispatch

Slag, like most other object-oriented languages, is single dispatch. This means that when a method is called, the exact variation of the method can vary dynamically depending on the type of the object it's called on but not on the type of the arguments (a single point of flexibility).

Single Dispatch example

 # Thanks to single dispatch, the printlns print the correct
 # monster name instead of just printing "Monster", even though
 # the variables m1 and m2 are of type Monster.
 class Test
   method init:
     local Monster m1 = Ghost()
     local Monster m2 = Goblin()
     println( m1.to_String )  # prints: Ghost
     println( m2.to_String )  # prints: Goblin
 endClass
 
 class Monster()
   method to_String.String:
     return "Monster"
 endClass
 
 class Ghost : Monster
   method to_String.String:
     return "Ghost"
 endClass
 
 class Goblin : Monster
   method to_String.String:
     return "Goblin"
 endClass

Where Single Dispatch fails

This example shows where double dispatch would be handy. Note that only the reference type (and not the true object type) of arguments is considered when deciding which method to call.

 class Test
   method init:
     local Monster m1 = Ghost()
     local Monster m2 = Goblin()
     poke(m1)  # prints: "poking Monster"
     poke(m2)  # prints: "poking Monster"
 
   method poke( Monster m ):
     println( "poking Monster" )
 
   method poke( Ghost g ):
     println( "poking Ghost" )
 
   method poke( Goblin g ):
     println( "poking Goblin" )
 
 endClass
 
 class Monster();
 class Ghost : Monster;
 class Goblin : Monster;

Double Dispatch

Double dispatch is a mechanism where we force the compiler to consider both the type of the object the method is called on and the true type of the argument sent to the method. Double dispatch is a form of multidispatch that operates on two axes of flexibility (the object and one argument).

General approach

Here is how you would achieve double dispatch in Slag using a technique that works in most languages:

 # Classic double dispatch
 class Test
   method init:
     local Monster m1 = Ghost()
     local Monster m2 = Goblin()
     m1.dispatch_poke(this)  # prints: "poking Ghost"
     m2.dispatch_poke(this)  # prints: "poking Goblin"
 
   method poke( Monster m ):
     println( "poking Monster" )
 
   method poke( Ghost g ):
     println( "poking Ghost" )
 
   method poke( Goblin g ):
     println( "poking Goblin" )
 
 endClass
 
 class Monster()
   METHODS
     method dispatch_poke( Test test ):
       # If we don't override this method, "this" is always type Monster
       test.poke(this)
 endClass
 
 class Ghost : Monster
   method dispatch_poke( Test test ):
     test.poke(this)
 endClass
 
 class Goblin : Monster
   method dispatch_poke( Test test ):
     test.poke(this)
 endClass

Double Dispatch with "propagated" methods

If you give a Slag method the "propagated" qualifier, the compiler will insert a new copy of the method into each class that inherits the method as if you had typed it in yourself. Our previous example becomes a bit smaller:

 # Double dispatch with propagated dispatch methods.
 class Test
   method init:
     local Monster m1 = Ghost()
     local Monster m2 = Goblin()
     m1.dispatch_poke(this)  # prints: "poking Ghost"
     m2.dispatch_poke(this)  # prints: "poking Goblin"
 
   method poke( Monster m ):
     println( "poking Monster" )
 
   method poke( Ghost g ):
     println( "poking Ghost" )
 
   method poke( Goblin g ):
     println( "poking Goblin" )
 
 endClass
 
 class Monster()
   METHODS
     propagated method dispatch_poke( Test test ):
       test.poke(this)
 endClass
 
 class Ghost : Monster;
 class Goblin : Monster;