9

In my application I need ability to swap elements in collection. So I have a choice:

  1. use mutable collection declared as val
  2. or use immutable collection declared as var (and always reassign new collection to var)

But in Scala (in functional programming) mutability always avoided. So what is less worse: mutable collection declared using val or immutable collection declared as var?

WelcomeTo
  • 19,843
  • 53
  • 170
  • 286
  • 1
    Without more context, it doesn't really make a difference. Neither is a functional approach. Mutable collection will probably perform better than an immutable var. – dbyrne Jul 23 '13 at 13:29
  • @dbyrne *Mutable collection will probably perform better than an immutable var* whoa? aren't those two play in the same team? e.g. `val misterMutableCollection = collection.mutable....` – om-nom-nom Jul 23 '13 at 13:46
  • 1
    @om-nom-nom With an immutable var, you'll have to construct an entirely new collection and swap it. With a mutable val, you can use the original collection and call methods to mutate it. – dbyrne Jul 23 '13 at 13:48

6 Answers6

10

This really depends on whether you need to share that collection widely. The advantage of a mutable collection is that it is usually faster than the immutable collection, and it's easier to have a single object to pass around rather than having to make sure you can set a var from different contexts. But since they can change out from under you, you have to be careful even in a single-threaded context:

import collection.mutable.ArrayBuffer
val a = ArrayBuffer(1,2,3,4)
val i = a.iterator
i.hasNext             // True
a.reduceToSize(0)
i.next                // Boom!

java.lang.IndexOutOfBoundsException: 0
at scala.collection.mutable.ResizableArray$class.apply(ResizableArray.scala:43)
    ...

So if it's going to be widely used, you should consider whether you can be appropriately careful to avoid problems like this. It's generally safer to use a var to an immutable collection; then you might get out of date, but at least you won't fall on your face with a segfault.

var v = Vector(1,2,3,4)
val i = v.iterator
i.hasNext            // True
v = Vector[Int]()
i.next               // 1

Now, however, you have to pass v as a return value from any method that might modify it (outside of the class that contains it, at least). This also can cause problems if you forget to update the original value:

var v = Vector(1,2,3,4)
def timesTwo = v.map(_ * 2)
timesTwo
v       // Wait, it's still 1,2,3,4?!

But then this doesn't update either, does it?:

a.map(_ * 2)    // Map doesn't update, it produces a new copy!

So, as a general rule,

  1. Performance requires you to use one -- use it
  2. Local scope within a method -- use mutable collection
  3. Shared with other threads/classes -- use immutable collection
  4. Implementation within a class, simple code -- use mutable collection
  5. Implementation within a class, complex code -- use immutable

but you should probably violate this as often as you stick to it.

Rex Kerr
  • 166,841
  • 26
  • 322
  • 407
4

If you use a var holding an immutable collection you can publish it fairly freely (though you may want to mark the var as @volatile). At any given time other code can get only a particular snapshot of that state which can never change.

If you use a val holding a mutable collection instance, then you must guard it carefully, since it can be witnessed in inconsistent states while being updated.

Randall Schulz
  • 26,420
  • 4
  • 61
  • 81
  • Can you share please a little more information on how @volatile works in Scala? From what i understand it flags that multiple threads may be accessing it at the same time, but I'm not sure what this annotation really changes or does to the variable? thanks – LaloInDublin Jul 23 '13 at 14:32
  • 1
    @volatile works exactly the same as the volatile keyword in java does. It works on the VM level. See http://stackoverflow.com/questions/2694439/how-does-volatile-actually-work for example. – Christophe Vanfleteren Jul 23 '13 at 14:50
1

Mutability is the beast that you would be better to keep in a cage. Ideally, in well-tested and highly used type of cage like scala mutable collection is.

om-nom-nom
  • 62,329
  • 13
  • 183
  • 228
1

One safe method I've used works something like this.. first hide your var to make it thread safe like this:

private[this] val myList: scala.collection.mutable.(whatever)

private[this] restricts a variable not only to this class, but only to this exact instance. No other variable can access it.

Next, make a helper function to return an immutable version of it whenever something external needs to work with it:

def myListSafe = myList.toList (or some other safe conversion function like toMap, etc)

You might get some overhead doing these conversions, but it gives you the flexibility of using a mutable collection safely within the class - while providing the chance to export it thread safe when needed. As you have pointed out the other option is to continuously mutate a var pointing to an immutable collection.

LaloInDublin
  • 5,379
  • 4
  • 22
  • 26
0

Reassignments to a var might be hard to read/maintain, especially if this happens at multiple locations in a method, for example.

So with a val, you get at least an immutable reference.

Generally speaking the scope of a mutable collection should be as small as possible. So after you have filled the mutable collection completely (in case of a short-lived buffer), you could turn it into an immutable one, for example to return this as the result of a method.

As dbyrne has pointed out correctly: If it's not possible to convert the mutable collection into an immutable one (if you are implementing a cache for example), and the mutable collection is part of the public API, then a var might be better. Another option in this case is to create an immutable copy in the method which implements the public API.

Beryllium
  • 12,808
  • 10
  • 56
  • 86
  • 1
    This is the best answer so far in my opinion, but there are cases where its more important for the collection itself to be immutable, and the reference is not so important. If you are a library author and you want to make sure that anyone using your API doesn't mutate a collection after you return it to them, an immutable var would be appropriate. – dbyrne Jul 23 '13 at 13:56
0

Agreed with Rex, the biggest concern is whether you are sharing the collection. In that case use immutable. Another alternative:

@volatile
private var myList = immutable.List.empty[Foo]
def theList = myList
sourcedelica
  • 23,940
  • 7
  • 66
  • 74