Reputation: 44436
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
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
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
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