3

Here is the code:

trait Foo {

  def get(x: Int): Int

}

trait Simple extends Foo {

  override def get(x: Int): Int = x

}

trait Add15 extends Foo {

  abstract override def get(x: Int): Int = x + 15

}

trait Add30 extends Foo {

  abstract override def get(x: Int): Int = {

    super.get(x) + 30
  }

}


class Queue extends Simple with Add15 with Add30

new Queue with Add30 get 0 // same as new Queue get 0, because with Add30 is ignored

I just wonder that is it better to have a compilation error instead of ignoring it ? it is just like

class Queue extends Simple with Add15 with Add30 with Add30 will have a compilation error

Many thanks in advance

DaoWen
  • 32,589
  • 6
  • 74
  • 101
Xiaohe Dong
  • 4,953
  • 6
  • 24
  • 53
  • possible duplicate of [Scala: How to inherit the same trait twice?](http://stackoverflow.com/questions/17287395/scala-how-to-inherit-the-same-trait-twice), or [Inheriting a trait twice](http://stackoverflow.com/questions/7230808/inheriting-a-trait-twice). – DaoWen Jun 19 '14 at 12:58
  • 1
    @DaoWen IMO this is a different question: if `Add30` is a normal trait then `new Queue with Add30` [documents the intent](http://stackoverflow.com/questions/4387419/why-does-arraylist-have-implements-list) that `Queue` is implementing `Add30` but does it hold true when trait is stackable? – Shyamendra Solanki Jun 19 '14 at 13:35
  • Whether this makes sense or not depends on the semantics of the stackable trait, and that's not something that's captured in the type system, so prohibiting it would rule out cases where you might legitimately want to be able to repeat the trait to document intent (as @ShyamendraSolanki says). – Travis Brown Jun 19 '14 at 15:15
  • Mixin traits don't work here, but composition (i.e. the Decorator pattern) works fine. See the way views stack in the Scala collections library for an example. – wingedsubmariner Jun 19 '14 at 18:21
  • Here, "document intent" != "doesn't generate extra bytecode". – som-snytt Jun 19 '14 at 18:33

1 Answers1

1

The with is not ignored.

The spec for new t actually says that new Queue with Add30 is equivalent to:

{ class a extends Queue with Add30 ; new a }

Sure, but does it actually compile to that?

In fact:

scala> new Queue with Add30
res8: Queue with Add30 = $anon$1@aa21042

scala> :javap -prv -
Binary file res8 contains $line18.$read$$iw$$iw$
[snip]
  private final $line12.$read$$iw$$iw$Queue res8;
    flags: ACC_PRIVATE, ACC_FINAL
[snip]
  public $line18.$read$$iw$$iw$();
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=1, args_size=1
         0: aload_0       
         1: invokespecial #19                 // Method java/lang/Object."<init>":()V
         4: aload_0       
         5: putstatic     #21                 // Field MODULE$:L$line18/$read$$iw$$iw$;
         8: aload_0       
         9: new           #23                 // class $line18/$read$$iw$$iw$$anon$1
        12: dup           
        13: invokespecial #24                 // Method $line18/$read$$iw$$iw$$anon$1."<init>":()V
        16: putfield      #17                 // Field res8:L$line12/$read$$iw$$iw$Queue;
        19: return   

So the resulting value is just a Queue, but you're instantiating an anonymous subclass that trivially extends it. I'm too lazy (I mean busy) to try -optimize.

You can turn the question around and ask why does with T with T complain about trait T is inherited twice? Can't linearization handle the redundancy?

I think the second case, where the a disappears from the linearization, is for the case:

scala> class X
defined class X

scala> trait Y extends X
defined trait Y

scala> new X with Y
res15: X with Y = $anon$1@5af97169

where you're mixing in a Y that already extends X.

But now I must use the napkin (serviette) on which I was scribbling for its primary use case and go back to what I was doing before.

som-snytt
  • 39,429
  • 2
  • 47
  • 129