views:

124

answers:

3

In Programming in Scala the authors write that Scala's == function compares value equality instead of reference equality.

This works as expected on lists:

scala> List(1,2) == List(1,2)
res0: Boolean = true

It doesn't however work on arrays:

scala> Array(1,2) == Array(1,2)
res1: Boolean = false

In Programming Scala the authors recommend to use the sameElements function instead:

scala> Array(1,2).sameElements(Array(1,2))
res2: Boolean = true

As an explanation they write:

While this may seem like an inconsistency, encouraging an explicit test of the equality of two mutable data structures is a conservative approach on the part of the language designers. In the long run, it should save you from unexpected results in your conditionals.

  1. What does this mean? What kind of "unexpected results" are they talking about? What else could I expect from an array comparison than to return true if the arrays contain the same elements in the same position? Why does the equals function work on lists but not on arrays?

  2. How can I make the equals function work on arrays?

A: 

It's all about referential transparency. The idea is, if two values are ==, it shouldn't matter which one you use for something. If you have two arrays with the same contents, it clearly matters which one you modify, so == returns false unless they are the same one.

SamB
This is not correct! Scala implements == for all collections except for Array. See my own answer.
olle kullberg
I thought I would use the `eq` function to compare references in Scala
Stefan Schmidt
Well, yes. Use the eq method to do that. But remember that when you create a class, (not a case class), then == will compare references if you do not override equals().
olle kullberg
@olle: I guess I've been doing too much Haskell?
SamB
My question was related to instantiating an `Array`. Why are you referring to "creating a class"?
Stefan Schmidt
What he meant (I think) is that "under the hood" actually the method `equals` is used to compare two objects. So if you implement a class you can overwrite `equals` and write your own equality-method. But as syntactic sugar Scala lets you write `==` instead of equals.
Plankalkül
+6  A: 

This exact question has been voiced many times (by myself too, see http://stackoverflow.com/questions/3213368/strange-behaviour-of-the-array-type ).

Note that it is ONLY the Array collection that does not support ==, all other collections do. The root cause is that Array IS the Java array.

olle kullberg
Why did the language designers choose the Java `array` to be the default `Array` instead of `ArraySeq` or `WrappedArray` which would behave as expected?
Stefan Schmidt
@Stefan - Because the others have to box primitive types, and thus don't run as fast--which is exactly when you'd want to use an array. Also, when using Java code, which many do, arrays abound. Thus the lesser of the evils is to make `Array` be the Java array; the cost is that `==` behaves inconsistently. Also, there is no obvious choice for `Array` otherwise. Should it be `ArraySeq`? How about `ArrayBuffer`? Maybe `WrappedArray`? Each one of those has decent reasons for existing, but none is the overwhelmingly obvious candidate.
Rex Kerr
So you're saying a) it was a performance choice b) I normally shouldn't be using arrays in Scala anyway so it doesn't matter that their `equals` function is inconsistent?
Stefan Schmidt
+4  A: 

It is true that the explanation offered in the book is questionable, but to be fair it was more believable when they wrote it. It's still true in 2.8, but we have to retrofit different reasoning because as you've noticed, all the other collections do element comparisons even if they're mutable.

A lot of blood had been shed trying to make Arrays seem like the rest of the collections, but this was a tremendously leaky abstraction and in the end it was impossible. It was determined, correctly I think, that we should go to the other extreme and supply native arrays the way they are, using implicit machinery to enhance their capabilities. Where this most noticeably falls down is toString and equals, because neither of them behaves in a reasonable fashion on Arrays, but we cannot intercept those calls with implicit conversions because they are defined on java.lang.Object. (Conversions only happen when an expression doesn't type check, and those always type check.)

So you can pick your explanation, but in the end arrays are treated fundamentally differently by the underlying architecture and there's no way to paper over that without paying a price somewhere. It's not a terrible situation, but it is something you have to be aware of.

extempore
So you're saying the language designers don't think that this inconsistency is remarkbly good design, but rather see this as the least painful compromise?
Stefan Schmidt
Speaking for myself, programming is little but "least painful compromises" not that this stops me from enjoying it. And speaking for the rest of the world, I'd say your paraphrase is a fair summary.
extempore