Implicit double dispatch

18 01 2008

While playing around with JCR and Scala I stumbled over JCR’s Value type. I wanted to be able to handle it in a type safe way by using Scala’s generics. This was not a problem until it came to collection of values. Here is a boiled down version of my problem not directly related to JCR. (The entire code is available on my code page under implicit double dispatch.

Scala allows for polymorphic methods by means of overloading:

def foo(s: String) { ... }
def foo(n: int) { ... }

Calling method foo with either a string or a integer argument will automatically result in a call to the method with the corresponding signature. However, this does not carry over to generic types:

def foo(x: List[String]) { ... }
def foo(x: List[int]) { ... }

Here the Scala compiler will complain about both methods having the same erasure. In a nutshell this means Scala (like Java) erases the type information from List and adds casts to the correct target type behind the scenes. However, this leaves us with the two methods having the same signature which is exactly what the compiler complains about.
If we want to handle lists of ints and strings in specific ways we could try the following:

def qoo[T](x: List[T]) {
  for(y <- x) {
    if (y.isInstanceOf&#91;String&#93;)
      println("Got a string: " + y.asInstanceOf&#91;String&#93;)
    else if (y.isInstanceOf&#91;int&#93;)
      println("Got a int: " + y.asInstanceOf&#91;int&#93;)
    else
      error("Type mismatch")
    }
}
&#91;/sourcecode&#93;
But that way we scarify type safety. What we need is a way to restrict the actual types allowed for the type parameter T to int and string. We can not achieve this using <a href="http://www.scala-lang.org/intro/upbounds.html">bounds</a> <a href="http://www.scala-lang.org/intro/lowbounds.html">since</a> int and string belong to disparate branches of the type lattice. However, in Scala generic type parameters can also be restricted by specifying a <a href="http://www.scala-lang.org/intro/views.html">view bound</a>. We can thus define

def foo[T <% Dispatcher&#91;T&#93;&#93;(x: T) { ... }
&#91;/sourcecode&#93;
for some type Dispatcher&#91;T&#93;. Now foo can be called for any type which is convertible to type Dispatcher&#91;T&#93;. That is, the Scala compiler searches for <a href="http://www.scala-lang.org/intro/implicit.html">implicit methods</a> which take an argument of type T and return a Dispatcher[T]. If we supply such implicit conversion methods for lists of ints and lists of strings we will be able to call foo passing arguments of such types only.

abstract class Dispatcher[T](value: T) {
  def select(switch: Switch)
  def dispatch(switch: Switch)
}

A Dispatcher[T] is involved in a double dispatch. First List[T] is (implicitly) converted to a Dispatcher[List[T]]. The select method of the latter converts each element of List[T] to a Dispatcher[T] instance an calls its dispatch method passing along a Switch instance.

trait Switch {
  def case_(s: String)
  def case_(n: int)
}

In the second step the dispatch method calls the case_ method on the Switch instance passing along the wrapped value. Finally we need the conversion methods for the implicit conversions:

implicit def string2Disp(value: String) = new Dispatcher(value){
def select(switch: Switch) {}
def dispatch(switch: Switch) {
switch.case_(value)
}
}
implicit def int2Disp(value: int) = new Dispatcher(value){
def select(switch: Switch) {}
def dispatch(switch: Switch) {
switch.case_(value)
}
}
implicit def list2Disp[T](value: List[T])(implicit x2Disp: T => Dispatcher[T]) =
new Dispatcher(value) {
def select(switch: Switch) {
for(x <- value) { x2Disp(x).dispatch(switch) } } def dispatch(switch: Switch) {} } [/sourcecode] Now we can use these conversions in the implementation of foo like this: [sourcecode language='java'] def foo[T <% Dispatcher[T]](x: T) { x.select(new Switch { def case_(x: String) { println("Got a string " + x) } def case_(x: int) { println("Got a int " + x) } }) } [/sourcecode] Calling foo will now succeed only on lists of ints and lists of strings: [sourcecode language='java'] foo("11"::"12"::Nil) foo(12::5::Nil) foo(12.12::3.15::Nil) [/sourcecode] The first two lines will compile fine while on the third line the Scala compiler reports an error since it cannot find a matching implicit conversion from List[double]) to Dispatcher[List[double]]. If strings and ints provided for a dispatch mechanism by themselves, I could have saved me the trouble by just using these. Instead I had to mimic extension methods for the former using Scala’s Pimp my library pattern.

Advertisements

Actions

Information

2 responses

8 02 2008
Implicit double dispatch revisited « Michid’s Weblog

[…] double dispatch revisited 8 02 2008 In my first post on this topic I showed how generic types can be treated polymorphically in Scala. I employed […]

31 05 2010
Working around type erasure ambiguities « Michid’s Weblog

[…] around type erasure ambiguities 30 05 2010 In an earlier post I already showed how to work around ambiguous method overloads resulting from type erasure. In a […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s




%d bloggers like this: