2016-06-05 20 views
5

Jestem zdezorientowany przez pisanie za kulisami w celu zrozumienia na mapach. Moje zrozumienie jest, że zewnętrzna typu kolekcja jest zwykle powinien być zachowany, i widzimy, że oczekiwane zachowanie w dwóch następujących przypadkach:Scala - nieoczekiwany typ przełączenia z mapy na Iterable w celu zrozumienia?

scala> for { 
    | (k,v) <- Map(0->1,2->3) 
    | } yield k -> v 
res0: scala.collection.immutable.Map[Int,Int] = Map(0 -> 1, 2 -> 3) 

scala> for { 
    | (k,v) <- Map(0->1,2->3) 
    | foo = 1 
    | } yield k -> v 
res1: scala.collection.immutable.Map[Int,Int] = Map(0 -> 1, 2 -> 3) 

Ale kiedy dodać drugie zadanie wewnątrz do zrozumienia, że ​​coś zaskakującego:

scala> for { 
    | (k,v) <- Map(0->1,2->3) 
    | foo = 1 
    | bar = 2 
    | } yield k -> v 
res2: scala.collection.immutable.Iterable[(Int, Int)] = List((0,1), (2,3)) 

Dlaczego tak się dzieje?

Odpowiedz

5

Jeśli uruchomisz scala -Xprint:typer -e "for { ... } yield k->v", otrzymasz wersję kodu pozbawioną słodyczy. Oto bardzo uproszczona wersja tego, co można dostać:

val m: Map[Int,Int] = Map(0->1, 2->3) 
m.map { 
    case x @ (k,v) => 
    val foo = 1 
    val bar = 2 
    (x, foo, bar) 
}.map { 
    case ((k,v), foo, bar) => (k, v) 
} 

Więc co można zauważyć jest to, że gdy za-zrozumienie zostanie przekonwertowany do .map rozmowy, to rzeczywiście powrocie foo i bar wraz z k->v , co oznacza, że ​​jest to Tuple3[(Int,Int), Int, Int]. Ponieważ obiekty o numerach Tuple3 nie mogą zostać przekształcone w Map, zakładają, że musi on zwrócić wartość Iterable. Jednak w celu uzyskania prawidłowego wyniku, który jest zbiorem obiektów Tuple2, wykonuje on wtórny .map, który odrzuca foo i bar z Tuple3, ale w tym momencie nie wie już, że powinien on być Map, ponieważ kiedy połączeń łańcuchowych do .map, że informacje nie są przenoszone do przodu.

Twój przykład z jednym zadaniem jest po prostu szczęśliwy, ponieważ reprezentacja pośrednia to Tuple2[(Int,Int), Int].

Z drugiej strony, jeśli używasz .map bezpośrednio, to działa:

Map(0->1, 2->3).map { 
    case (k,v) => 
    val foo = 1 
    val bar = 2 
    k -> v 
}