Scala: var List vs val MutableList

2020-08-10 09:35发布

In Odersky et al's Scala book, they say use lists. I haven't read the book cover to cover but all the examples seem to use val List. As I understand it one also is encouraged to use vals over vars. But in most applications is there not a trade off between using a var List or a val MutableList?. Obviously we use a val List when we can. But is it good practice to be using a lot of var Lists (or var Vectors etc)?

I'm pretty new to Scala coming from C#. There I had a lot of:

public List<T> myList {get; private set;}

collections which could easily have been declared as vals if C# had immutability built in, because the collection itself never changed after construction, even though elements would be added and subtracted from the collection in its life time. So declaring a var collection almost feels like a step away from immutability.

In response to answers and comments, one of the strong selling points of Scala is: that it can have many benefits without having to completely change the way one writes code as is the case with say Lisp or Haskell.

5条回答
看我几分像从前
2楼-- · 2020-08-10 09:42

Is it good practice to be using a lot of var Lists (or var Vectors etc)?

I would say it's better practice to use var with immutable collections than it is to use val with mutable ones. Off the top of my head, because

  • You have more guarantees about behaviour: if your object has a mutable list, you never know if some other external object is going to update it

  • You limit the extent of mutability; methods returning a collection will yield an immutable one, so you only have mutablility within your one object

  • It's easy to immutabilize a var by simply assigning it to a val, whereas to make a mutable collection immutable you have to use a different collection type and rebuild it

In some circumstances, such as time-dependent applications with extensive I/O, the simplest solution is to use mutable state. And in some circumstances, a mutable solution is just more elegant. However in most code you don't need mutability at all. The key is to use collections with higher order functions instead of looping, or recursion if a suitable function doesn't exist. This is simpler than it sounds. You just need to spend some time getting to know the methods on List (and other collections, which are mostly the same). The most important ones are:

map: applies your supplied function to each element in the collection - use instead of looping and updating values in an array

foldLeft: returns a single result from a collection - use instead of looping and updating an accumulator variable

for-yield expressions: simplify your mapping and filtering especially for nested-loop type problems

Ultimately, much of functional programming is a consequence of immutability and you can't really have one without the other; however, local vars are mostly an implementation detail: there's nothing wrong with a bit of mutability so long as it cannot escape from the local scope. So use vars with immutable collections since the local vars are not what will be exported.

查看更多
劳资没心,怎么记你
3楼-- · 2020-08-10 09:51

Here is how I see this problem of mutability in functional programming.

Best solution: Values are best, so the best in functional programming usage is values and recursive functions:

val myList = func(4); 
def func(n) = if (n>0) n::func(n) else Nil

Need mutable stuff: Sometimes mutable stuff is needed or makes everything a lot easier. My impression when we face this situation is to use the mutables structures, so to use val list: collection.mutable.LinkedList[T] instead of var list: List[T], this is not because of a real improvement on performances but because of mutable functions which are already defined in the mutable collection.

This advice is personal and maybe not recommended when you want performance but it is a guideline I use for daily programming in scala.

查看更多
贪生不怕死
4楼-- · 2020-08-10 09:51

If it is necessary to use var lists, why not? To avoid problems you could for example limit the scope of the variable. There was a similar question a while ago with a pretty good answer: scala's mutable and immutable set when to use val and var.

查看更多
欢心
5楼-- · 2020-08-10 09:53

I believe you can't separate the question of mutable val / immutable var from the specific use case. Let me deepen a bit: there are two questions you want to ask yourself:

  1. How am I exposing my collection to the outside?
    1. I want a snapshot of the current state, and this snapshot should not change regardless of the changes that are made to the entity hosting the collection. In such case, you should prefer immutable var. The reason is that the only way to do so with a mutable val is through a defensive copy.
    2. I want a view on the state, that should change to reflect changes to the original object state. In this case, you should opt for an immutable val, and return it wrapped through an unmodifiable wrapper (much like what Collections.unmodifiableList() does in Java, here is a question where it's asked how to do so in Scala). I see no way to achieve this with an immutable var, so I believe your choice here is mandatory.
    3. I only care about the absence of side effects. In this case the two approaches are very similar. With an immutable var you can directly return the internal representation, so it is slightly clearer maybe.
  2. How am I modifying my collection?
    1. I usually make bulk changes, namely, I set the whole collection at once. This makes immutable var a better choice: you can just assign what's in input to your current state, and you are fine already. With an immutable val, you need to first clear your collection, then copy the new contents in. Definitely worse.
    2. I usually make pointwise changes, namely, I add/remove a single element (or few of them) to/from the collection. This is what I actually see most of the time, the collection being just an implementation detail and the trait only exposing methods for the pointwise manipulation of its status. In this case, a mutable val may be generally better performance-wise, but in case this is an issue I'd recommend taking a look at Scala's collections performance.
查看更多
甜甜的少女心
6楼-- · 2020-08-10 09:56

You are assuming either the List must be mutable, or whatever is pointing to it must be mutable. In other words, that you need to pick one of the two choices below:

val list: collection.mutable.LinkedList[T]
var list: List[T]

That is a false dichotomy. You can have both:

val list: List[T]

So, the question you ought to be asking is how do I do that?, but we can only answer that when you try it out and face a specific problem. There's no generic answer that will solve all your problems (well, there is -- monads and recursion -- but that's too generic).

So... give it a try. You might be interested in looking at Code Review, where most Scala questions pertain precisely how to make some code more functional. But, ultimately, I think the best way to go about it is to try, and resort to Stack Overflow when you just can't figure out some specific problem.

查看更多
登录 后发表回答