I'm new to Scala, and I'm struggling to understand why I sometimes don't get a type error when supplying the wrong argument to Set.contains
Here's a quick example using the REPL (2.9.1.final):
scala> val baz = Map("one" -> 1, "two" -> 2)
baz: scala.collection.immutable.Map[java.lang.String,Int] = Map(one -> 1, two -> 2)
scala> baz.values.toSet.contains("asdf")
res3: Boolean = false
Why didn't I get a type mismatch there?
If I assign baz.values.toSet
to another val
, and call contains
on that, I do get type checking:
scala> val bling = baz.values.toSet
bling: scala.collection.immutable.Set[Int] = Set(1, 2)
scala> bling.contains("asdf")
<console>:10: error: type mismatch;
found : java.lang.String("asdf")
required: Int
bling.contains("asdf")
^
Stupid mistake, language subtlety, or compiler bug?
OK, so Set
is invariant in its type parameter and it works exactly like it should
scala> Set(1, 2, 3) contains "Hi"
<console>:8: error: type mismatch;
found : java.lang.String("Hi")
required: Int
Set(1, 2, 3) contains "Hi"
^
But, like you say:
scala> Map('a -> 1, 'b -> 2, 'c -> 3).values.toSet contains "Hi"
res1: Boolean = false
The only conclusion we can reasonably come to is that the type of the Set
in question is not Set[Int]
. What happens if we explicitly tell scala we want a Set[Int]
? The same piece of code with an explicit type parameter works just fine (i.e. it does not compile):
scala> Map('a -> 1, 'b -> 2, 'c -> 3).values.toSet[Int] contains "Hi"
<console>:8: error: type mismatch;
found : java.lang.String("Hi")
required: Int
Map('a -> 1, 'b -> 2, 'c -> 3).values.toSet[Int] contains "Hi"
^
The issue is the inferred type parameter being passed to the toSet
method. Scala is obviously taking the contains "Hi"
into account and inferring the lub of Int
and String
(i.e. Any
)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With