2014-10-07 10 views
10

Kann jemand erklären, warum das Folgende nicht funktioniert. Irgendwie verliert das kompilieren einige Informationen für die Typschlussfolgerung, wenn ich toSet mache, aber ich verstehe nicht warum.toSet und Typ Inferenz

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

Scheint, dass der Compiler einige Hilfe braucht, um den Typ explizit zu machen, 'ids.toSet.map (Foo (_: Int," bar "))' – jarandaf

+0

Ja, aber warum braucht der Compiler die Informationen nicht, wenn ich es tue die nach der Karte setzen ?? – regexp

+1

Verwirrenderweise funktioniert das "val a = ids.toSet; a.map (Foo (_, "bar")) ' – elm

Antwort

6

Angenommen, ich habe folgendes bekam:

trait Pet { 
    def name: String 
} 

case class Dog(name: String) extends Pet 

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

Set nicht in seiner Typparameter kovariant, aber List ist. Das heißt, wenn ich eine List[Dog] habe, habe ich auch eine List[Pet], aber eine Set[Dog] ist nicht eine Set[Pet]. Der Einfachheit halber ermöglicht Scala, dass Sie während einer Konvertierung von List (oder anderen Sammlungstypen) in Set mit einem expliziten Typparameter auf toSet upcast. Wenn Sie val a = ids.toSet; a.map(...) schreiben, wird dieser Typparameter abgeleitet und Ihnen geht es gut. Wenn Sie jedoch schreiben ids.toSet.map(...), wird es nicht abgeleitet, und Sie haben kein Glück.

Dies ermöglicht die folgenden arbeiten:

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

Während dies nicht:

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

Ist das lohnt sich die Verwirrung? Ich weiß es nicht. Aber es macht irgendwie Sinn, und es ist die Entscheidung, die die Collections API Designer für toSet getroffen haben, also sind wir damit festgefahren.

+0

Eine verwirrende Sache ist, dass die Unfähigkeit, einen Typ für zu schließen 'toSet' wird vom Compiler als Unfähigkeit ausgedrückt, auf einen Typ für' Foo (_, "bar") 'zu schließen. Die Inferenz scheint in beiden Richtungen zu funktionieren: 'ids.toSet [Int] .map (Foo (_," bar "))' beschweren sich nicht, und auch nicht 'ids.toSet.map (Foo (_: Int, "bar")) '. –

+0

@Joe: Das Problem ist, dass 'map' den Typ des Arguments seines Arguments kennen muss. Wenn der Typ der aufgerufenen Menge nicht bekannt ist oder noch nicht abgeleitet wurde, müssen Sie diesen Typ explizit angeben. –

Verwandte Themen