tags:

views:

130

answers:

4

Suppose I have the following code:

trait Trait1 { 
  trait Inner {
    val name = "Inner1"
  }
}

trait Trait2 {
  trait Inner {
    val name = "Inner2"
  }
}

class Foo extends Trait1 with Trait2 {
  // I want Concrete1 to be a Trait1.Inner not a Trait2.Inner
  class Concrete1 extends Inner
  val c = new Concrete1
}

object Obj {
  def main(args: Array[String]): Unit = {
    val foo = new Foo
    println(foo.c.name)
  }
}

When I mix in Trait1 and Trait2, referring to Inner seems to default to the Inner type of whichever trait I mixin second; so when I call Obj's main method it prints Inner2. How can I refer to Trait1.Inner in Foo? All three of the following give compiler errors:

class Concrete1 extends Trait1.Inner
class Concrete1 extends Trait1$Inner
class Concrete1 extends Trait1#Inner
+2  A: 

Why not order the traits in the order you expect them to have precedence? The linearization of traits is not arbitrary, but specified.

Daniel
Yes, I'd thought of doing that. But suppose I want to create a Concrete2 extends Trait2.Inner as well. What then?
ams
+4  A: 

There are two namespaces within a template (template being the body of a class, object, or trait.)

  1. Members: vals, vars, and defs and nested objects
  2. Types: types aliases, nested traits and nested classes

When inheriting from multiple parent templates, conflicts in these namespaces are resolved through class linearization.

You could re-order your inheritance to bring the desired parent Inner into your class, or find an alternative design.

retronym
Thanks, it didn't occur to me that member traits would be overridden. Makes sense when you think about it!
ams
+3  A: 

One option (if you can be invasive to the traits) is to define each Inner trait as a type member that has a non-conflicting name.

trait Trait1 {
  type Inner1 = Inner
  trait Inner {
    val name = "Inner1"
  }
}

trait Trait2 {
  type Inner2 = Inner
  trait Inner {
    val name = "Inner2"
  }
}

class Foo extends Trait1 with Trait2 {
  class Concrete1 extends Inner1
  class Concrete2 extends Inner2
  val c1 = new Concrete1
  val c2 = new Concrete2
}

object App extends Application {
  val foo = new Foo
  println(foo.c1.name) // Inner1
  println(foo.c2.name) // Inner2
}

If you cannot be invasive to the original traits (Trait1 and Trait2), you can extend them to define the type member.

trait Trait1 {
  trait Inner {
    val name = "Inner1"
  }
}
trait Trait2 {
  trait Inner {
    val name = "Inner2"
  }
}

trait Trait1a extends Trait1 {
  type Inner1 = Inner
}
trait Trait2a extends Trait2 {
  type Inner2 = Inner
}

class Foo extends Trait1a with Trait2a {
  class Concrete1 extends Inner1
  class Concrete2 extends Inner2
  val c1 = new Concrete1
  val c2 = new Concrete2
}

Another approach would be to use an intermediate trait to define your first concrete class:

trait Trait1 {
  trait Inner {
    val name = "Inner1"
  }
}
trait Trait2 {
  trait Inner {
    val name = "Inner2"
  }
}

trait FooIntermediate extends Trait1 {
  class Concrete1 extends Inner
}

class Foo extends FooIntermediate with Trait2 {
  class Concrete2 extends Inner
  val c1 = new Concrete1
  val c2 = new Concrete2
}
Mitch Blevins
Thanks, I'd thought of doing type Inner1 = Inner. I hadn't thought of your other two options though; now I have too many choices :)
ams
+4  A: 

Instead of

class Concrete1 extends Inner

Use this

class Concrete1 extends super[Trait1].Inner

That should get you what you want

Mushtaq Ahmed
That's great, I can now write class Concrete1 extends super[Trait1].Inner and class Concrete2 extends super[Trait2].Inner and have instances of both types of Inner in Foo.
ams