答えは の定義にありmap
ます:
def map[B, That](f : (A) => B)(implicit bf : CanBuildFrom[Repr, B, That]) : That
2 つのパラメーターがあることに注意してください。1 つ目は関数で、2 つ目は暗黙的です。その暗黙的な指定がない場合、Scala は利用可能な最も具体的なものを選択します。
約breakOut
それで、の目的はbreakOut
何ですか?質問の例を考えてみましょう。文字列のリストを取得し、各文字列を tuple に変換し(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]
です。これらの型はすべて知っているので、暗黙の type が必要であると判断でき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は aMap('a' -> 1).map(_._1)
を返すことはできませんMap
(それは an を返します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 つ目は aMap
を返し、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
ただし、最後のものが優先されます。したがって、 immutable の定義と、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]
例に戻ると、最初の map は type の関数を((Int, String)) => (Int, Int)
受け取り、2 番目の map は 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
、クラスがによって拡張された object 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]
であるため、 on と の型は次のよう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]]
。