Ответ находится на определение map:
def map[B, That](f : (A) => B)(implicit bf : CanBuildFrom[Repr, B, That]) : That
Обратите внимание, что у него есть два параметра. Первая - это ваша функция, а вторая - неявная. Если вы не укажете это неявно, Scala выберет наиболее конкретный из доступных.
Около breakOut
Итак, какова цель breakOut? Рассмотрим пример, приведенный для вопроса. Вы берете список строк, преобразуете каждую строку в кортеж (Int, String)и затем создаетеMap из него. Наиболее очевидный способ сделать это - создать промежуточную List[(Int, String)]коллекцию, а затем преобразовать ее.
Учитывая, что для создания результирующей коллекции mapиспользуется a 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преобразования каждого элемента) и сохранения результатов в новой коллекции.
Чтобы максимизировать повторное использование кода, это сохранение результатов осуществляется с помощью builder ( scala.collection.mutable.Builder), который в основном поддерживает две операции: добавление элементов и возврат результирующей коллекции. Тип этой результирующей коллекции будет зависеть от типа компоновщика. Таким образом, Listстроитель вернет a List, Mapстроитель вернет a Mapи так далее. Реализацияmap метода не должна зависеть от типа результата: об этом заботится строитель.
С другой стороны, это означает, что mapэтого строителя нужно как-то получить. Проблема, с которой столкнулись при разработке Scala 2.8 Collections, заключалась в том, как выбрать лучшего строителя из возможных. Например, если бы я написал Map('a' -> 1).map(_.swap), я хотел бы получить Map(1 -> 'a')обратно. С другой стороны, Map('a' -> 1).map(_._1)не может вернуть Map(он возвращает Iterable).
Магия создания наилучшего Builderиз известных типов выражений осуществляется через это CanBuildFromнеявное.
Около CanBuildFrom
Чтобы лучше объяснить, что происходит, я приведу пример, где отображаемая коллекция - это Mapвместо а List. Я вернусь к Listпозже. А пока рассмотрим эти два выражения:
Map(1 -> "one", 2 -> "two") map Function.tupled(_ -> _.length)
Map(1 -> "one", 2 -> "two") map (_._2)
Первое возвращает a, Mapа второе возвращает 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]
Возвращаясь к примеру, первая карта получает функцию типа, ((Int, String)) => (Int, Int)а вторая карта получает функцию типа ((Int, String)) => String. Я использую двойные скобки, чтобы подчеркнуть, что получен кортеж, Aкак мы уже видели.
С этой информацией, давайте рассмотрим другие типы.
map Function.tupled(_ -> _.length):
B = (Int, Int)
map (_._2):
B = String
Мы можем видеть, что тип, возвращаемый первым map- это Map[Int,Int], а второй - 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. Во втором случае, поскольку первый не совпал, он выбрал второй CanBuildFrom.
Вернуться к вопросу
Давайте посмотрим на код вопроса, определения Lists и 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")is List[String], поэтому типы Aи Reprопределены на TraversableLike:
A = String
Repr = List[String]
Тип для (x => (x.length, x))is (String) => (Int, String), поэтому тип Bis:
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.