Scala indexOf accepts everything

I wrote this in REPL:

case class Thingy(s: String) val things = List(Thingy("x"), Thingy("y")) things.indexOf("x") 

Returns -1 . But I would expect it to not compile, because "x" is String , not Thingy . Actually, it turns out no matter what type you type in indexOf :

 things.indexOf(42) things.indexOf(java.time.LocalDate.now()) 

They all return -1.

indexOf has this signature:

 def indexOf[B >: A](elem: B): Int 

I thought >: means B must be a supertype of A , but none of these classes is a supertype of my case Thingy class.

What am I missing here?

+5
source share
1 answer

B displays as java.io.Serializable , which is a supertype of both String and Thingy . 1

This is an unfortunate consequence of two things:

  • Scala are redundant parent types that share all / most of the values ​​( Any , Object , Serializable , etc.).
  • List is covariant. 2

To define indexOf as

 def indexOf(elem: A): Int 

would put A in a contravariant position, which is not allowed 3 because it violates the Liskov substitution principle: A List[Thingy] is List[Any] , and you can call .indexOf("x") on a List[Any] , so you should be able to call .indexOf("x") on the List[Thingy] .


1 If they were unable to implement Serializable , it would still output Any .

2 This is the basis for the preference for invariant collections, such as scalaz.IList .

3 Try it - it will not compile: trait Foo[+A] { def bar(a: A) }

+7
source

All Articles