ToSet и тип вывода

Может кто-нибудь объяснить, почему следующее не работает. Как-то теряется компиляция некоторой информации для вывода типа, когда я делаю toSet, но я не понимаю, почему.

scala> case class Foo(id: Int, name: String)
defined class Foo

scala> val ids = List(1,2,3)
ids: List[Int] = List(1, 2, 3)

scala> ids.toSet.map(Foo(_, "bar"))
<console>:11: error: missing parameter type for expanded function ((x$1) => Foo(x$1, "bar"))
              ids.toSet.map(Foo(_, "bar"))
                                ^

scala> ids.map(Foo(_, "bar")).toSet
res1: scala.collection.immutable.Set[Foo] = Set(Foo(1,bar), Foo(2,bar), Foo(3,bar))

Ответ 1

Предположим, у меня есть следующее:

trait Pet {
  def name: String
}

case class Dog(name: String) extends Pet

val someDogs: List[Dog] = List(Dog("Fido"), Dog("Rover"), Dog("Sam"))

Set не является ковариантным в своем параметре типа, но List is. Это означает, что если у меня есть List[Dog], у меня также есть List[Pet], но a Set[Dog] не является Set[Pet]. Для удобства Scala позволяет вам ускорить преобразование из List (или других типов коллекций) в Set путем предоставления явного параметра типа на toSet. Когда вы пишете val a = ids.toSet; a.map(...), этот параметр типа выводится, и вы в порядке. Когда вы пишете ids.toSet.map(...), с другой стороны, это не выводится, и вам не повезло.

Это позволяет работать следующим образом:

scala> val twoPetSet: Set[Pet] = someDogs.toSet.take(2)
twoPetSet: Set[Pet] = Set(Dog(Fido), Dog(Rover))

Пока это не так:

scala> val allDogSet: Set[Dog] = someDogs.toSet
allDogSet: Set[Dog] = Set(Dog(Fido), Dog(Rover), Dog(Sam))

scala> val twoPetSet: Set[Pet] = allDogSet.take(2)
<console>:14: error: type mismatch;
 found   : scala.collection.immutable.Set[Dog]
 required: Set[Pet]
Note: Dog <: Pet, but trait Set is invariant in type A.
You may wish to investigate a wildcard type such as `_ <: Pet`. (SLS 3.2.10)
       val twoPetSet: Set[Pet] = allDogSet.take(2)
                                               ^

Неужели это стоит путаницы? Я не знаю. Но это имеет смысл, и это решение дизайнеров API коллекций сделано для toSet, поэтому мы застряли с ним.