3

I found this reading another question:

"[...] a trait that extends a class puts a restriction on what classes can extend that trait - namely, all classes that mix-in that trait must extend that class"

a little example:

class C 
trait U
trait T extends C 

class D extends U with T // does not work
class E extends T with U // works

Apparently when traits inherit from classes, you have to put the trait in the position that you would otherwise place a class in (i.e. directly after extends)

Now to my questions:

(extended the previous example)

class C 
class Test
trait U
trait T extends C 
trait Tst extends Test


class D extends U with T // does not work
class E extends T with U // works
class Test2 extends Tst with T
  • what do we do when we want to inherit form two different traits, where each of those two inherit from a different class? (see class Test 2) this doesn't seem to be possible
  • if we need to pay attention to the placement of traits that extend a class, how do traits work then? Are traits inheriting from classes not "normal" traits anymore?
user6454491
  • 158
  • 9
  • 2
    I find traits inherting classes to be a bit peculiar. Do you have a concrete example showing why this is nessecary at all? – Yuval Itzchakov Jun 24 '16 at 22:27
  • unfortunately not. I just read this in an example and was wondering myself. I'm new to scala, though not to OOP, and am yet to encounter something like this. I found it strange that the "position" of the trait can have an influence on the correctness, despite inheriting just from traits (as opposed to one class and multiple traits) – user6454491 Jun 24 '16 at 22:54

2 Answers2

4
  1. This is indeed impossible. You cannot inherit from two different classes at once, whether via traits or otherwise. This is unfortunate, but true. There is no very good reason for that AFAICT, it would be somewhat harder to implement, but not impossible, at least for scala (not "native java") classes. So apparently, it was just decided against at some point, seemingly without a good reason.

  2. Traits that extend classes aren't really "abnormal". It's more like the way scala compiler handles them is. Basically, anything, that is or extends a class has to appear in the extends clause, and not in with. Why? Well, why can you not compare Strings in java with ==? because ...

Dima
  • 39,570
  • 6
  • 44
  • 70
  • 1
    The reason that Scala disallows multiple inheritance is because of the Diamond Problem. As usually Wikipedia has a good writeup: https://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem – Mark Kegel Jun 24 '16 at 23:46
  • 2
    @littlenag Technically Scala doesn't disallow multiple inheritance as long as it can be linearized. You can easily create diamond shape with traits, but it won't have C++ problem (i.e. `super` will be defined). – Victor Moroz Jun 25 '16 at 00:03
  • 1
    @littlenag scala does _not_ disallow multiple inheritance. Giving "class" a different name ("trait") does not solve the diamond problem (or any problem at all for that matter) – Dima Jun 25 '16 at 00:34
  • 1
    @VictorMoroz regarding "super being defined", it's not as straitforward as you might think. Check this out for example: http://stackoverflow.com/questions/36182817/meaning-of-super-in-stacked-traits-depends-on-call-site – Dima Jun 25 '16 at 00:37
  • 1
    Of course it's not straightforward, but it's deterministic: http://www.artima.com/pins1ed/traits.html#fig:linearization – Victor Moroz Jun 25 '16 at 01:23
  • 1
    @VictorMoroz it is deterministic in any (sane) language. Including C++, SmallTalk etc. No news here. And no advantage from disallowing multiple inheritance from classes. There is no semantical difference between classes and traits. The difference is just an artifact of a particular implementation on top of the JVM. I know how linearization works, it does not help the problem described in the link I gave above (which, I believe, is really just a bug in scala compiler more than any kind of a language feature). – Dima Jun 25 '16 at 01:30
  • 1
    No, in C++ there is no equivalent of `super` namely because of the multiple inheritance. – Victor Moroz Jun 25 '16 at 01:53
  • 1
    @Dima you mentioned Smalltalk... classic Smalltalk doesn't have traits or multiple-inheritance. Pharo St has traits. In Pharo traits are mixed into the final class description. It means two things (for Pharo): 1. trait definitions doesn't allow fields, otherwise you can end with class shape problems (ie compiled methods in St have references to fields in class def). 2. `super` doesn't have any particular meaning for traits, they apply to the method dispatch of the class hierarchy. So in that case there is a semantic difference between trait composition and multi-inheritance. – Diego Jun 25 '16 at 04:22
  • 1
    @VictorMoroz you are wrong: there is still linearization in C++, so it _is_ possible to have a notion of super. The reason it does not exist, is not multiple inheritance, but a design decision to give programmer ability to reference any parent class in the hierarchy explicitly, not just the one immediately preceding in the linearized sequence. – Dima Jun 25 '16 at 12:11
  • 1
    @Diego: yes, I misspoke about Smalltalk, my bad. It is an interesting point about semantic difference between trait composition and inheritance. You seem to be suggesting that `super` means something different in class context than it does in trait context. Can you take a look at this question to see if this observation is applicable? http://stackoverflow.com/questions/36182817/meaning-of-super-in-stacked-traits-depends-on-call-site – Dima Jun 25 '16 at 12:16
  • @Dima I'd love to see an example of C++ superclass linearization. When you call them explicitly it's not a linearization. Yes, you can implement it, but it won't be C++. `super` should then point to a sibling among other cases, like `C -> B` in my example, can you call sibling method in C++? I believe you can't. – Victor Moroz Jun 25 '16 at 18:26
  • @VictorMoroz the wikipedia page you pointed to earlier has some examples. You are right, when you call explicitly, there is no need for linearization, but when calling `foo.bar`, if `bar` isn't defined in `Foo` explicitly, linearization is needed to figure out which `bar` to call. – Dima Jun 25 '16 at 18:41
  • @Dima Scala could go for general support of multiple inheritance IF and ONLY IF it were to completely drop compatibility with Java, and side step fundamental details of the JVM, like invokevirtual. Because Scala tries to maintain some semblance of Java interop it has to play by the same rules as Java, which means that classes (as defined at the JVM level) can have only one super-class. Traits are a Scala-only notion which means Scala can support and enforce some version of multiple inheritance, but when they inherit from classes they again have to play by Java's/JVMs rules. – Mark Kegel Jun 26 '16 at 16:24
2

Here's my guess: class can be defined with parameters, now let's imagine this:

class A(val x: Int) 
class B extends A(1)
class C extends A(2)
trait D extends B
trait E extends C

// Oops! x = ? Doesn't compile of course
class F extends D with E

UPDATE:

Disclaimer: I'm not an expert in C++

Here's how C++ is solving the diamond problem:

class A {
  int x;

public:
  A(int _x) {  x = _x; }
  int getX() { return x; };
};

class B : virtual public A {    
public:
  // B b; b.getX == 1
  B() : A(1) {}
};

class C : virtual public A {    
public:
  // C c; c.getX == 2
  C() : A(2) {}
};  

class D : public B, public C {
public:
  // I need to know that B/C inherit A
  // A(...) constructors defined above don't apply
  D(): B(), C(), A(3) {}
};

I guess that's exactly the problem Scala is trying to avoid.

Victor Moroz
  • 9,167
  • 1
  • 19
  • 23