2014-10-07 10 views
10

Czy ktoś może wyjaśnić, dlaczego poniższe nie działa. W pewnym sensie tracę kompilację informacji na temat wnioskowania o typ, kiedy robię to, co robię, gdy robię toSet, ale nie rozumiem dlaczego.toSet i typ wnioskowania

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

Wygląda na to, że kompilator potrzebuje pomocy w sprecyzowaniu typu: 'ids.toSet.map (Foo (_: Int," bar "))' – jarandaf

+0

Tak, ale dlaczego nie kompilator potrzebuje informacji, kiedy robię toSet po mapie? – regexp

+1

To działa myląco, 'val a = ids.toSet; a.map (Foo (_, "bar")) ' – elm

Odpowiedz

6

Załóżmy mam następujące:

trait Pet { 
    def name: String 
} 

case class Dog(name: String) extends Pet 

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

Set nie kowariantna w jego parametr typu, ale List jest. Oznacza to, że jeśli mam List[Dog], mam także List[Pet], ale Set[Dog] jest a . Dla wygody, Scala pozwala na upcast podczas konwersji z List (lub innych typów kolekcji) na Set poprzez podanie jawnego parametru typu na toSet. Podczas pisania val a = ids.toSet; a.map(...), ten typ parametru jest wywnioskowany i wszystko w porządku. Z drugiej strony, kiedy piszesz ids.toSet.map(...), nie jest to wnioskowane i nie masz szczęścia.

Pozwala to dodaje do pracy:

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

Chociaż nie:

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

Czy to warte zamieszania? Nie wiem Ale to ma sens, i to jest decyzja, którą projektanci kolekcji API wykonali dla toSet, więc utknęliśmy z tym.

+0

Jedną z mylących rzeczy jest to, że nie można wywnioskować typu dla 'toSet' jest wyrażany przez kompilator jako niezdolność do wyprowadzenia typu dla' Foo (_, "bar") '. Inferencja wydaje się działać w dowolnym kierunku: 'ids.toSet [Int] .map (Foo (_," bar "))' nie narzeka, i nie robi 'ids.toSet.map (Foo (_: Int, "bar")) '. –

+0

@Joe: Problem polega na tym, że 'map' musi znać typ argumentu jego argumentu. Jeśli typ zestawu, dla którego jest wywoływany, nie jest znany lub nie został jeszcze wywnioskowany, musisz podać ten typ jawnie. –

Powiązane problemy