25

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

Eugene Yokota
  • 94,654
  • 45
  • 215
  • 319
Seth Tisue
  • 29,985
  • 11
  • 82
  • 149
  • Is List generic in Scala, so that it only accepts items of one type? – Lasse V. Karlsen Sep 09 '10 at 17:31
  • Presumably scala does it this way because that's how java does it (`Collection.contains` also takes an argument of type `Object`, not `T`), though admittedly that's not much of an answer. – sepp2k Sep 09 '10 at 17:38

3 Answers3

23

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 C. Sobral
  • 295,120
  • 86
  • 501
  • 681
  • Ah. Others have made interesting points (thanks all!), but I bet this is the real reason. – Seth Tisue Sep 09 '10 at 18:42
  • 6
    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 Sep 09 '10 at 18:46
  • 1
    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 Sep 09 '10 at 18:51
  • Just use `-Xfatal-warnings` to make it fail compilation – Joan Jul 09 '19 at 02:49
4

"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
  • 20,435
  • 3
  • 55
  • 76
  • 2
    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 Sep 09 '10 at 18:05
  • 1
    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 Sep 10 '10 at 11:35
1

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.

denis phillips
  • 12,550
  • 5
  • 33
  • 47
  • 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 Sep 09 '10 at 18:05
  • 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 Sep 09 '10 at 18:07
  • @Danny Actually, `==` *is* the Scala equivalent of Java `equals()`. As Dave Griffith explained, both Java and Scala use untyped equality. – Aaron Novstrup Sep 09 '10 at 18:12
  • @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. – denis phillips Sep 09 '10 at 18:12