Type inference fails on Set made with .toSet?

Q: Why doesn’t toSet do what I want?

A: That would be too easy.

Q: But why doesn’t this compile? List(1).toSet.map(x => ...)

A: The Scala compiler is unable to infer that x is an Int.

Q: What, is it stupid?

A: Well, List[A].toSet doesn’t return an immutable.Set[A]. It returns an immutable.Set[B] for some unknown B >: A.

Q: How was I supposed to know that?

A: From the Scaladoc.

Q: But why is toSet defined that way?

A: You might be assuming immutable.Set is covariant, but it isn’t. It’s invariant. And the return type of toSet is in covariant position, so the return type can’t be allowed to be invariant.

Q: What do you mean, “covariant position”?

A: Let me Wikipedia that for you: http://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science) . See also chapter 19 of Odersky, Venners & Spoon.

Q: I understand now. But why is immutable.Set invariant?

A: Let me Stack Overflow that for you: Why is Scala’s immutable Set not covariant in its type?

Q: I surrender. How do I fix my original code?

A: This works: List(1).toSet[Int].map(x => ...). So does this: List(1).toSet.map((x: Int) => ...)

(with apologies to Friedman & Felleisen. thx to paulp & ijuma for assistance)

EDIT: There is valuable additional information in Adriaan’s answer and in the discussion in the comments both there and here.

Leave a Comment