toSet and type inference

前端 未结 1 1772
无人及你
无人及你 2021-01-02 06:17

Can somebody explain why the following does not work. Somehow looses the compile some information for the type inference when i do toSet, but i don\'t understan

相关标签:
1条回答
  • 2021-01-02 06:39

    Suppose I've got the following:

    trait Pet {
      def name: String
    }
    
    case class Dog(name: String) extends Pet
    
    val someDogs: List[Dog] = List(Dog("Fido"), Dog("Rover"), Dog("Sam"))
    

    Set isn't covariant in its type parameter, but List is. This means if I have a List[Dog] I also have a List[Pet], but a Set[Dog] is not a Set[Pet]. For the sake of convenience, Scala allows you to upcast during a conversion from a List (or other collection types) to a Set by providing an explicit type parameter on toSet. When you write val a = ids.toSet; a.map(...), this type parameter is inferred and you're fine. When you write ids.toSet.map(...), on the other hand, it's not inferred, and you're out of luck.

    This allows the following to work:

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

    While this doesn't:

    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)
                                                   ^
    

    Is this worth the confusion? I don't know. But it kind of makes sense, and it's the decision the Collections API designers made for toSet, so we're stuck with it.

    0 讨论(0)
提交回复
热议问题