Michael Lorton
Michael Lorton

Reputation: 44436

How can I be modifying a Nil list?

You know that scene in Goodbye and Thanks For All The Fish where Arthur is so deliriously happy he stops a waiter and demands to know, "Why is this food so good?" I'm in that situation. Scala seems to be doing exactly what I'd want, but I don't understand how it's doing it. Consider the following:

scala> var v = Nil:List[String];
v: List[String] = List()

scala> v.length
res38: Int = 0

scala> v ::= "Hello"

scala> v.length
res39: Int = 1

scala> Nil.length
res40: Int = 0

Which is exactly what you'd hope for, but how is it happening?

Nil is an object that extends List[Nothing], which is a subtype of List[String], so assignment works fine, but it's an immutable list, isn't it? So I shouldn't be able to append to it. But I can append to it, or at least I can append to v, which I thought point to Nil. v is changed, but Nil isn't.

So, WTF? Does Scala have some clever copy-on-modify semantics I'm unaware of? Is Nil really a function that returns empty lists? More broadly, is there some way I could have made the REPL answer these questions?

Upvotes: 6

Views: 495

Answers (4)

Daniel C. Sobral
Daniel C. Sobral

Reputation: 297305

When Scala sees

v ::= "Hello"

it first checks if v knows the method ::=. In this case (type List), it doesn't, so, because the method consists only of symbols and ends with an = sign, it tries something else:

v = v.::("Hello")

which, incidentally, is written "Hello" :: v in infix notation. Now, since v knows the method ::, that works and returns a new List, which is then assigned to v as indicated.

By the way, it is prepending, not appending.

Upvotes: 16

ssanj
ssanj

Reputation: 2179

Nil is immutable. When you cons to it (::) you get a new instance which is also immutable.

Try this:

val v1 = Nil
val v2 = "Hello" :: v1
v1 == v2 

(you will get false since they don't point to the same object)

Given v is a var you can reassign values to it.

So when you have:

v ::= "Hello" // what you really have is:
v = "Hello" :: v  // or
v = "Hello" :: Nil // or
v = List("Hello")

The above creates a new List and Nil is left unchanged. It is similar to String addition in Java. Since String is immutable you never change a single instance - only create new ones.

Since Nil has not changed, Nil.length = 0.

Upvotes: 2

Vlad Patryshev
Vlad Patryshev

Reputation: 71

Try it with val v, to see what's mutable. :)

Upvotes: 3

wheaties
wheaties

Reputation: 35990

When you use the keyword var you're not "translating to final" in the Java sense. Instead, var allows for reassignment. When you call the operator ::= you're actually reassigning what v points to. The operator ::= returns a new list, not the original list with "Hello" appended to it. Hence, v is now pointing to "Hello" and not a Nil list.

Here, watch this:

var myThing = new List(1, 2, 3)
var myOhterThing = myThing

myThing = new List(1, 2, 3, 4)

It's almost the same as saying "take the first list, copy it and append a '4' onto it. Now assign 'myThing' to point to that list." Using that operator you have effectively done the same thing. Writing it out this way lets you see that.

Upvotes: 4

Related Questions