curious
curious

Reputation: 2928

Why Scala compiler throws IndexOutOfBoundException while applying foreach on a mutable list

I have the following Code.

import scala.collection.mutable.MutableList
val x = MutableList[Int]()
(1 to 10).foreach(x+=1)

I get the java.lang.IndexOutOfBoundsException: 1 error.

but,

(1 to 10).foreach(println) this does not throw any error.

the indexOutOfBoundException can be solved by using lambda Operator as follows:

(1 to 10).foreach(_ => x+=1)

Everything works fine with this.

My questions are :
1. Why do i need to use lambda operator in first case unlike second one?
2. Why the compiler throws IndexOutOfBoundException, i suppose this is not the correct context for this Exception.

Upvotes: 4

Views: 179

Answers (1)

Didier Dupont
Didier Dupont

Reputation: 29548

What happens is a few little conveniences in the library conspiring to bite you.

foreach signature is

def foreach(f: A => Unit): Unit

In your case, A is Int, so it needs a function which takes an Int and returns Unit. Fine with println. Note you have just written println, not println(something), which would not have been a function.

One would expect x += 1 to be just an instruction, so it would have type Unit, not a function, not a valid argument of foreach, and one would get a helpful compile-time error. But += in MutableList actually returns the list, which is convenient as it makes chaining operations easier:

def +=(elem: A): this.type

So the type of x+= 1 is MutableList[Int]. One will still expect a compilation error, not a function. Except that MutableLists (all Seqs actually) are functions, with Int parameters, returning the type of the Seq's elements. The function simply returns the i-th element. Again, that may be convenient, you may simply pass the seq where a function is expected, instead of having to write i => seq(i). Now you have a function, with an Int parameter, what foreach expects.

Still, it does not returns Unit, but Int. However, scala will accept that as an Int => Unit, just discarding the value. So it compiles.

Now to what it does: first, it evaluates the argument in foreach, so it calls x+=1, getting the list x, which now contains an element. It will then call this function, which is the access to the i-th element with arguments ranging from 1 to 10). Doing that, it would not add values to the list, but just access elements at the given indexes. It then fails immediately, as the list contains just one element, at index 0, so calling with 1 throws the IndexOutOfBoundException.

Upvotes: 5

Related Questions