--
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