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 o开发者_开发技巧bject 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?
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.
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.
Try it with val v, to see what's mutable. :)
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.
精彩评论