答えは次の定義にありmap
ます:
def map[B, That](f : (A) => B)(implicit bf : CanBuildFrom[Repr, B, That]) : That
2つのパラメーターがあることに注意してください。1つ目は関数で、2つ目は暗黙的です。その暗黙を提供しない場合、Scalaは利用可能な最も具体的なものを選択します。
約 breakOut
それで、目的はbreakOut
何ですか?質問の例を考えてみましょう。文字列のリストを受け取り、各文字列をタプルに変換(Int, String)
してMap
から、それを出力します。それを行う最も明白な方法は、中間List[(Int, String)]
コレクションを作成し、それを変換します。
をmap
使用しBuilder
て結果のコレクションを生成する場合、中間をスキップしList
て結果を直接に収集することはできませんMap
か?明らかに、そうです。これを行うには、しかし、我々は適切な合格する必要があるCanBuildFrom
としmap
、それが正確に何であるbreakOut
し。
それでは、次の定義を見てみましょうbreakOut
。
def breakOut[From, T, To](implicit b : CanBuildFrom[Nothing, T, To]) =
new CanBuildFrom[From, T, To] {
def apply(from: From) = b.apply() ; def apply() = b.apply()
}
breakOut
はパラメータ化されており、のインスタンスを返すことに注意してくださいCanBuildFrom
。偶然にも、種類はFrom
、T
とTo
私たちはそれを知っているので、すでに、推論されているmap
期待していますCanBuildFrom[List[String], (Int, String), Map[Int, String]]
。したがって:
From = List[String]
T = (Int, String)
To = Map[Int, String]
結論として、breakOut
それ自体が受け取った暗黙のものを調べてみましょう。タイプCanBuildFrom[Nothing,T,To]
です。これらの型はすべて知っているので、暗黙の型が必要であると判断できCanBuildFrom[Nothing,(Int,String),Map[Int,String]]
ます。しかし、そのような定義はありますか?
CanBuildFrom
の定義を見てみましょう。
trait CanBuildFrom[-From, -Elem, +To]
extends AnyRef
したがってCanBuildFrom
、最初の型パラメーターは反変です。のでNothing
下のクラスがあり、その手段(すなわち、それはすべてのサブクラスである)任意のクラスの代わりに使用することができますNothing
。
そのようなビルダーが存在するため、Scalaはそれを使用して目的の出力を生成できます。
ビルダーについて
Scalaのコレクションライブラリのメソッドの多くは、元のコレクションを取得し、何らかの方法で処理して(map
各要素を変換する場合)、結果を新しいコレクションに格納することで構成されています。
コードの再利用を最大化するために、この結果の保存はビルダー(scala.collection.mutable.Builder
)を介して行われます。ビルダーは、基本的に、要素の追加と結果のコレクションを返す2つの操作をサポートします。この結果のコレクションのタイプは、ビルダーのタイプによって異なります。このように、List
戻りますビルダーはList
、Map
ビルダーが戻りますMap
ように、と。map
メソッドの実装は、結果のタイプに関係する必要はありません。ビルダーがそれを処理します。
一方、それはmap
どういうわけかこのビルダーを受け取る必要があることを意味します。Scala 2.8コレクションを設計するときに直面した問題は、可能な限り最高のビルダーを選択する方法でした。たとえば、と書いた場合Map('a' -> 1).map(_.swap)
、Map(1 -> 'a')
取り戻したいと思います。一方、a Map('a' -> 1).map(_._1)
はa を返すことができませんMap
(それはを返しますIterable
)。
Builder
既知のタイプの式から可能な限り最良のものを生成する魔法は、このCanBuildFrom
暗黙のうちに実行されます。
約 CanBuildFrom
何が起こっているのかをよりよく説明するために、マッピングされているコレクションがのMap
代わりにである例を示しますList
。後で戻りますList
。ここでは、次の2つの式について考えます。
Map(1 -> "one", 2 -> "two") map Function.tupled(_ -> _.length)
Map(1 -> "one", 2 -> "two") map (_._2)
1つ目はa Map
を返し、2つ目はを返しますIterable
。フィッティングコレクションを返す魔法はの仕事ですCanBuildFrom
。map
それを理解するために再びの定義を考えてみましょう。
メソッドmap
はから継承されTraversableLike
ます。これは、上でパラメータ化されたB
とThat
し、型パラメータを使用するA
とRepr
、クラスをパラメータ化、。両方の定義を一緒に見てみましょう:
クラスTraversableLike
は次のように定義されます。
trait TraversableLike[+A, +Repr]
extends HasNewBuilder[A, Repr] with AnyRef
def map[B, That](f : (A) => B)(implicit bf : CanBuildFrom[Repr, B, That]) : That
どこA
からRepr
来てどこから来たかを理解するために、Map
それ自体の定義を考えてみましょう:
trait Map[A, +B]
extends Iterable[(A, B)] with Map[A, B] with MapLike[A, B, Map[A, B]]
そのためTraversableLike
拡張するすべての特性によって継承されMap
、A
そしてRepr
それらのいずれかから継承することができます。ただし、最後の方が優先されます。したがって、不変の定義とMap
それをTraversableLike
に接続するすべての特性に従って、次のようになります。
trait Map[A, +B]
extends Iterable[(A, B)] with Map[A, B] with MapLike[A, B, Map[A, B]]
trait MapLike[A, +B, +This <: MapLike[A, B, This] with Map[A, B]]
extends MapLike[A, B, This]
trait MapLike[A, +B, +This <: MapLike[A, B, This] with Map[A, B]]
extends PartialFunction[A, B] with IterableLike[(A, B), This] with Subtractable[A, This]
trait IterableLike[+A, +Repr]
extends Equals with TraversableLike[A, Repr]
trait TraversableLike[+A, +Repr]
extends HasNewBuilder[A, Repr] with AnyRef
Map[Int, String]
チェーンのずっと下にある型パラメーターを渡すとTraversableLike
、に渡され、したがってによって使用される型は次のようになりmap
ます。
A = (Int,String)
Repr = Map[Int, String]
例に戻ると、最初のマップはtypeの関数を受け取り((Int, String)) => (Int, Int)
、2番目のマップはtypeの関数を受け取っています((Int, String)) => String
。二重括弧は、受け取ったタプルであることを強調するために使用していA
ます。
その情報をもとに、他のタイプについて考えてみましょう。
map Function.tupled(_ -> _.length):
B = (Int, Int)
map (_._2):
B = String
最初から返されるタイプmap
はMap[Int,Int]
で、2番目はであることがわかりIterable[String]
ます。見てみるmap
の定義、これらがの値であることを確認するために簡単ですThat
。しかし、彼らはどこから来たのでしょうか?
関連するクラスのコンパニオンオブジェクトの内部を見ると、それらを提供する暗黙の宣言がいくつかあります。オブジェクト上Map
:
implicit def canBuildFrom [A, B] : CanBuildFrom[Map, (A, B), Map[A, B]]
そしてIterable
、そのクラスが以下によって拡張されるオブジェクトについてMap
:
implicit def canBuildFrom [A] : CanBuildFrom[Iterable, A, Iterable[A]]
これらの定義は、パラメーター化されCanBuildFrom
たのファクトリーを提供します。
Scalaは、利用可能な最も具体的な暗黙のものを選択します。最初のケースでは、それが最初CanBuildFrom
でした。2番目のケースでは、最初のケースが一致しなかったため、2番目のケースを選択しましたCanBuildFrom
。
質問に戻る
型の推論方法を確認するために、質問のコードList
のとmap
の定義を(もう一度)見てみましょう。
val map : Map[Int,String] = List("London", "Paris").map(x => (x.length, x))(breakOut)
sealed abstract class List[+A]
extends LinearSeq[A] with Product with GenericTraversableTemplate[A, List] with LinearSeqLike[A, List[A]]
trait LinearSeqLike[+A, +Repr <: LinearSeqLike[A, Repr]]
extends SeqLike[A, Repr]
trait SeqLike[+A, +Repr]
extends IterableLike[A, Repr]
trait IterableLike[+A, +Repr]
extends Equals with TraversableLike[A, Repr]
trait TraversableLike[+A, +Repr]
extends HasNewBuilder[A, Repr] with AnyRef
def map[B, That](f : (A) => B)(implicit bf : CanBuildFrom[Repr, B, That]) : That
タイプがList("London", "Paris")
あるList[String]
ので、種類A
やRepr
上に定義されたがTraversableLike
、次のとおりです。
A = String
Repr = List[String]
のタイプ(x => (x.length, x))
は(String) => (Int, String)
なので、のタイプB
は次のとおりです。
B = (Int, String)
最後の未知That
の型はの結果の型であり、map
すでにそれも持っています:
val map : Map[Int,String] =
そう、
That = Map[Int, String]
つまりbreakOut
、必ず、のタイプまたはサブタイプを返す必要がありCanBuildFrom[List[String], (Int, String), Map[Int, String]]
ます。
List
が、それにmap
。