views:

258

answers:

4

Why is the error below? How to workaround it?

EDIT: I assumed that since A and B compile to (interface,class) pairs, it's a matter of choosing the right static method call to implement when compiling C. I would expect the priority to be according to order.

scala> trait A {def hi = println("A")}
defined trait Ascala> trait A {def hi = println("A")}
defined trait A

scala> trait B {def hi = println("B")}
defined trait B

scala> class C extends B with A
:6: error: error overriding method hi in trait B of type => Unit;
 method hi in trait A of type => Unit needs `override' modifier
       class C extends B with A

scala> trait A {override def hi = println("A")}
:4: error: method hi overrides nothing
       trait A {override def hi = println("A")}

EDIT: note that in Ruby this works well:

>> module B; def hi; puts 'B'; end; end
=> nil
>> module A; def hi; puts 'A'; end; end
=> nil
>> class C; include A; include B; end
=> C
>> c = C.new
=> #
>> c.hi
B
=> nil
+4  A: 

You could use a common base trait:-

  trait Base {def hi: Unit}
  trait A extends Base {override def hi = println("A")}
  trait B extends Base {override def hi = println("B")}

  val a = new A {}
  val b = new B {}

  a.hi
  b.hi

  class C extends A with B

  val c = new C

  c.hi
Don Mackenzie
Thanks. I was kind of hoping for a not-intrusive solution. Something that can be done in C alone (as if A and B are from different third party libraries)
IttayD
+1  A: 

This is the diamond problem. Which method hi should be inherited, the one from A, or the one from B? You can get around this as Don suggested, by using a common base trait.

David Crawshaw
See my Ruby example, where everything is OK. It's a matter of deciding the semantics of 'super'. I can suggest that in the tree of extended traits, the first DFS wise is selected. Note also there's no diamond here. Just A,B,C. On the contrary, the solution is to create a Base trait, thus creating a diamond. So a diamond is a solution, not a problem here
IttayD
There is no diamond problem in Scala, due to [class linearization](http://jim-mcbeath.blogspot.com/2009/08/scala-class-linearization.html)...
paradigmatic
+1  A: 

A trait adds methods to the class that mixes it in. If two traits adds the same method, the class would end up with two identical methods, which, of course, can't happen.

If the method is private in the trait, however, it won't cause problem. And if you want the methods to stack over each other, you may define a base trait and then abstract override on the inheriting traits. It requires a class to define the method, however. Here is an example of this:

scala> trait Hi { def hi: Unit }
defined trait Hi

scala> trait A extends Hi { abstract override def hi = { println("A"); super.hi } }
defined trait A

scala> trait B extends Hi { abstract override def hi = { println("B"); super.hi } }
defined trait B

scala> class NoHi extends Hi { def hi = () }
defined class NoHi

scala> class C extends NoHi with B with A
defined class C

scala> new C().hi
A
B

If, however, you truly want two separate methods from each trait, then you'll need to compose instead of inherit.

Daniel
Why not just have the compiler select one method as the implementing one? Since A and B are compiled to interface A and class A$class with static methods, then in the JVM it is perfectly all right for C to implement both A and B and the implemetation of the method hi in C can just call A$class.hi
IttayD
Note that in your answer, C ends up with two (even three) identical methods, so this thing can be done.
IttayD
If one were willing to throw away the conceptual basis for inheritance, yes, it could be done -- and it could be regretted later. If you want composition, compose, don't inherit.
Daniel
I said the compiler can choose arbitrarily the method to use and just noted this is possible in the underlying implementation. In Mitch's solution, you need to explicitly write "override def hi = super[B].hi". What's wrong with the compiler doing it for you as a *default* implementation (that is, you can still override with whatever you want)
IttayD
+5  A: 

This works for me in 2.8, and would allow you to be non-intrusive in traits A or B:

trait A { def hi = println("A") }
trait B { def hi = println("B") }

class C extends A with B {
  override def hi = super[B].hi
  def howdy = super[A].hi // if you still want A#hi available
}

object App extends Application {
  (new C).hi // prints "B"
}
Mitch Blevins
Excellent! Too bad that if I try 'class C extends A with B', the error doesn't mention this way of solving the conflict.
IttayD