tags:

views:

423

answers:

2

I'm trying to get a better grasp of structural type dispatch. For instance, assume I have an iterable object with a summary method that computes the mean. So o.summary() gives the mean value of the list. I might like to use structural type dispatch to enable summary(o).

  1. Is there a set of best practices regarding o.summary() vs. summary(o)?
  2. How does scala resolve summary(o) if I have a method summary(o: ObjectType) and summary(o: { def summary: Double})
  3. How does structural type dispatch differ from multimethods or generic functions?

Michael Galpin gives the following discription about structural type dispatch:


Structural types are Scala’s version of “responds-to” style programming as seen in many dynamic languages. So like

def sayName ( x : { def name:String }){
    println(x.name)
}

Then any object with a method called name that takes no parameters and returns a string, can be passed to sayName:

case class Person(name:String)
val dean = Person("Dean")
sayName(dean) // Dean


+3  A: 

-- 1. In your example, I wouldn't use the summary(o) version, as this is not a very object oriented style of programming. When calling o.summary (you could drop the brackets as it has no side-effects), you are asking for the summary property of o. When calling summary(o), you are passing o to a method that calculates the summary of o. I believe that the first approach is nicer :).

I haven't used structural type dispatch much, but I assume that it is best suited (in a large system) for the case where you would have to write an interface just because one method wants a type that has some method defined. Sometimes creating that interface and forcing the clients to implement it can be awkward. Sometimes you want to use a client defined in another API which conforms to your interface but doesn't explicitly implement it. So, in my opinion, structural type dispatch serves as a nice way to make the adapter pattern implicitly (saves on boilerplate, yay!).

-- 2. Apparently if you call summary(o) and o is of ObjectType, summary(o: ObjectType) gets called (which does make sense). If you call summary(bar), in which bar is not of ObjectType, two things can happen. The call compiles if bar has the method summary() of the right signature and name or otherwise, the call doesn't compile.

Example:

scala> case class ObjectType(summary: Double)
defined class ObjectType

scala> val o = ObjectType(1.2)
o: ObjectType = ObjectType(1.2)

scala> object Test {
     | def summary(o: ObjectType)  { println("1") }
     | def summary(o: { def summary: Double}) { println("2")}
     | }
defined module Test

scala> Test.summary(o)
1

Unfortunately, something like the following does not compile due to type erasure:

scala> object Test{
     | def foo(a: {def a: Int}) { println("A") }
     | def foo(b: {def b: Int}) { println("B") }
     | }
:6: error: double definition:
method foo:(AnyRef{def b(): Int})Unit and
method foo:(AnyRef{def a(): Int})Unit at line 5
have same type after erasure: (java.lang.Object)Unit
       def foo(b: {def b: Int}) { println("B") }

-- 3. In a sense, structural type dispatch is more dynamic than generic methods, and also serves a different purpose. In a generic method you can say either: a. I want something of any type; b. I want something of a type that is a subtype of A; c. I'll take something that is a supertype of B; d. I'll take something that has an implicit conversion to type C. All of these are much stricter than just "I want a type that has the method foo with the right signature". Also, structural type dispatch does use reflection, as they are implemented by type erasure.

I don't know much about multimethods, but looking at the wikipedia article, it seems that multimethods in Scala can be achieved using pattern matching. Example:

def collide(a: Collider, b: Collider) = (a, b) match {
    case (asteroid: Asteroid, spaceship: Spaceship) => // ...
    case (asteroid1: Asteroid, asteroid2: Asteroid) => // ...
...

Again, you could use structural type dispatch - def collide(a: {def processCollision()}), but that depends on a design decision (and I would create an interface in this example).

-- Flaviu Cipcigan

Flaviu Cipcigan
Thanks, this is helpful. On your last point, I believe multimethods seek to avoid case/switch type statements because then all the types that use collide must be known by the api designer.
Tristan
+3  A: 

Structural data types aren't really all that useful. That's not to say they are useless, but they definitely a niche thing.

For instance, you might want to write a generic test case for the "size" of something. You might do it like this:

def hasSize(o: { def size: Int }, s: Int): Boolean = {
  o.size == s
}

This can then be used with any object that implements the "size" method, no matter its class hierarchy.

Now, they are NOT structural type dispatches. They are NOT related to dispatching, but to type definition.

And Scala is an object oriented language always. You must call methods on objects. Function calls are actually "apply" method calls. Things like "println" are just members of objects imported into scope.

Daniel