Scala val and var with collections

95 views Asked by At

I want to understand better the following issue. When should i use var and when val?

I know that there is a rule of thumb in Scala that we should use val.

For primitive types it's easy - we should use var only if we change a variable in a loop.

My first question is if there any better way to do that in Scala:

var sum = 0
while (condition) sum = sum + something
return sum

But same and more complicated is for collections.

  1. We have the mutable or immutable collections
  2. We have var and val for the pointers.

I want to ask for each of the following if it's make sense (means could be, but not necessarily good practice), and when should we use each of the following options (what is good practice):

  1. val mutable collection
  2. var mutable collection
  3. val immutable collection (can't be)
  4. var immutable collection

I also want to ask if changing a collection means to change it's pointer or it's elements, or both. Take a list for example. If I add an element I did a change. But what if I just want to update an element?

The reason I'm asking this question is that when we need to solve common problems, we need to change the collection and we can't use immutable val.

var list = List(1,2,3)
// this will not save the list with changes
while (condition) list :+ some_integer
return list

This topic is confusing for me in Scala.

I would appreciate if someone can make this topic more clear. Thanks.

I want to understand this topic better. In others languages it's quiet clear, but in Scala it's a bit confusing.

2

There are 2 answers

0
Seth Tisue On

In your example code:

var sum = 0
while (condition) sum = sum + something
return sum

How one might do this without var really depends on what condition is. We can't give you a one-size-fits-all answer.

But I'll give you one specific example. Suppose we have an imperative method getNext() that returns an Int, and we want to stop summing once it returns a -1. In Java one might write:

int sum = 0;
int next;
while ((next = getNext()) != -1) {
  sum += next;
}
return sum;

In Scala I would write:

Iterator.continually(getNext())
  .takeWhile(_ != -1)
  .sum

here I'm using higher-order methods such as continually and takeWhile. If the shape of the problem were different, different higher-order methods might be the right ones.

As an aside, note that I did not use return. 99% of Scala code never uses return at all.

4
Muhammad Farag On

That was my pain when I started learning Scala. So, I understand where you are coming from. The short answer: val with immutable collections, and create a new copy each time you want to change it.

Oversimplifying: For an immutable object or collection, when you want to make an update you (or the language under the hood) create a copy of that collection. The new copy will share the unchanged data with the old collection or object. So, it is memory efficient. Since the collection is immutable, and everything (pointer) in that collection points to something that is immutable. You should be safe making those references knowing that whatever you are referring to is not going to change.

So the way you'd structure Scala code using val differs from how you'd structure your Scala code using var. And because of that difference, this code snippet you gave will not be written this way. I am not sure what you are trying to achieve so, it is hard for me to rewrite it.

Let's say you want to add someInteger to the list if a condition applies to all elements of the list.

val newList = if (list.forall(elem => elem > 4)) list :+ someInteger else list

What is important here, is you have a new list that you would return.

And because of the rich collections and all the methods you have, you usually do not see traditional while and for loops in Scala code. What you might see in some cases is recursion.

Allow me to give another example

case class Account(value: Int){
  def deposit(amount: Int): Account = {
    this.copy(value = this.value + amount)
  }
}

val account = Account(0)
val updatedAccount = account.deposit(10)

This example shows two things:

  1. updating account value required a copy of the account because value is immutable.
  2. you need a new val updatedAccount to store the updated account.

This is the way you'd usually write code in Scala.

Finally, let's go through a list of accounts and close any account that has balance below zero

val updatedAccounts = accounts.map(account => if (account.value <0) account.close else account)

and in all the examples above we are using vals with immutable collections. I hope this is helpful.