views:

113

answers:

3

So for example why does List(1,2,3,4).contains("wtf") even compile? Wouldn't it be nice if the compiler rejected this?

+1  A: 

SeqLike.contains checks whether a value is present by checking for an element in the sequence that is equal to the value (using ==). == takes an Any so I suspect that this is the reason.

dpp
Yeah, the equivalence relation does not require two objects to have the same type. It's arguable that the `contains` method should cater to the common case (where equal objects have the same type) since the `exists` method could be used to implement a more general test, though.
Aaron Novstrup
That's not really a reason, is it. There's been a concious decision to use == rather than whatever the Scala equivalent of .equals() is. So the real answer lies in the decision rather than in the implementation.
dty
@Danny Actually, `==` *is* the Scala equivalent of Java `equals()`. As Dave Griffith explained, both Java and Scala use untyped equality.
Aaron Novstrup
@Danny: I agree with that to a degree. At some point in the design you have to ask what does it mean to contain an item and specifying that they are equal with respect to == makes sense. Of course, you could constrain what contains accepts and still use == so this is where the decision comes in to play (as you point out). The fact that == accepts Any probably influenced the decision.
dpp
+2  A: 

"contains" is fundamentally about equality testing, and equality in Scala (as in Java before it) is untyped. The practical value of having untyped-equality is small, but not zero. There are, for instance, a few occasions where it makes sense for two objects of different classes to be equal to one another. For instance, you might wish an object of type RGBColor to be equal to a PantoneColor if they define the same hue, or an immutable HashSet and an immutable TreeSet to be equal if they contain the same elements. That said, untyped-equality also causes a bunch of headaches, and the fact that the compiler could easily catch that List(1,2,3,4).contains("wtf") is nonsensical but won't is one of them.

Most Java bug-finding tools include tests to detect the presence of improbable untyped-equality uses. (I wrote the inspections to do this in IntelliJ IDEA.) I have no doubt that when Scala bug-finding tools come online, these will be among the first bugs detected.

Dave Griffith
Oh, I should mention that if you want to play around with typed equality instead of untyped equality, check out the Equals construct in the scalaz library. In a perfect world, collections would be parameterizable over their equality testing predicate, but in the real world libraries aren't quite there yet.
Dave Griffith
One usecase I found: `List(1,2,3,4).contains(BigInt("2"))` results in `true`. Personally I think that could have been achieved with implicits/type classes instead of making `contains`/`exists` take `Any` ...
soc
+7  A: 

Lots of interesting answers, but here's my own theory: if contains did not receive an Any, then Seq could not be co-variant.

See, for instance, Set, which is not co-variant and whose contains take an A instead of an Any.

The reasons for that is left as an exercise to the reader. ;-) But here is a hint:

scala> class Container[+A](elements: A*) {                         
     |   def contains(what: A): Boolean = elements exists (what ==)
     | }
<console>:7: error: covariant type A occurs in contravariant position in type A of value what
         def contains(what: A): Boolean = elements exists (what ==)
                      ^
Daniel
Ah. Others have made interesting points (thanks all!), but I bet this is the real reason.
Seth Tisue
And to answer Daniel's "exercise for the reader": if Seq is covariant, then e.g. a Seq[Int] must qualify as a Seq[Any], so then I must be able to pass anything to its contains method.
Seth Tisue
Oh, and then I wanted to know why Set is invariant. That's answered here: http://stackoverflow.com/questions/676615/why-is-scalas-immutable-set-not-covariant-in-its-type
Seth Tisue