答えは次の定義にあり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。