1
votes

Is it possible to build an Immutable List through inheritance in Scala? I have a base trait which creates a MenuBar. The MenuBar will have the standard headers: File, Edit, View / Window, Help etc. I want inheriting traits to be able to add sub-items to the menu headings. I want the Menus to be created dynamically, so there will be a list of functions which will be called when the menu is opened to create the sub tree. As the list of functions will be known at compile time it would be nice if it could be an immutable list.

Is there any way in Scala to create an immutable list through a constructor/ initialisation hierarchy? If not in Scala has any language offered this feature?

To illustrate the problem with a simple example using a mutable list. TrA and TrB are written independently of each other:

trait Base
{
  val list = scala.collection.mutable.LinkedList[String]()
}
trait TrA extends Base
{
  list += "A"
}
trait TrB extends Base
{
  list += "B"
}

val ab = new TrA with TrB {}

As the contents of the List are known at compile time is there anyway to make it a val immutable list? Of course any mutable collection can be made public as an immutable collectiion through a def call.

The following will compile:

trait Base
{ val list: List[String] = Nil }

trait TrA extends Base
{ val list = "A" :: super.list }

trait TrB extends Base
{ val list = "B" :: super.list }

val ab = new TrA with TrB {}

But it can't be initialised without throwing a null exception. Making val list lazy doesn't help either, hence the necessity for 0__ 's solution.

2
Could you please provide a sample (pseudo-)code showing what would you like to achieve?Petr
@PetrPudlák hopefully added code explains problem.Rich Oliver

2 Answers

4
votes

This is very similar to an older question I remember; I couldn't find it anymore, so I tried to reconstruct the approach:

trait Base {
  protected def contribute : List[String] = Nil

  val list = contribute
}

trait TrA extends Base {
   override protected def contribute = "A" :: super.contribute
}

trait TrB extends Base {
   override protected def contribute = "B" :: super.contribute
}

val x = new TrA with TrB {}
x.list  // List(B, A)
val y = new TrB with TrA {}
y.list  // List(A, B)
0
votes

I'd hide the mutable part of the list so that only descendants of Base can add to it (and nothing else):

trait Base {
  private val theList = scala.collection.mutable.LinkedList[String]()

  protected def add(item: String) {
    theList += item;
  }

  // return only an immutable copy of the list
  def list: Seq[String] = theList.toSeq; // or .toList, .toSet, etc.
}

trait TrA extends Base {
  add("Apple")
}

trait TrB extends Base {
  add("Orange")
}

While ill-behaved descendants still could add some items later, it's impossible to modify the list from outside. Maybe it'd be possible to replace def list with lazy val list to gain some efficiency, but you have to be sure that it's not called in any of the constructors.