答案在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 使用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 是一个底层类(即它是所有事物的子类),这意味着可以使用any 类来代替Nothing。
由于存在这样的构建器,Scala 可以使用它来生成所需的输出。
关于建设者
Scala 集合库中的许多方法包括获取原始集合,以某种方式对其进行处理(在 map 的情况下,转换每个元素),然后将结果存储在新集合中。
为了最大限度地重用代码,这种结果存储是通过 builder (scala.collection.mutable.Builder) 完成的,它基本上支持两种操作:添加元素和返回结果集合。此结果集合的类型将取决于构建器的类型。因此,List 构建器将返回 List,Map 构建器将返回 Map,依此类推。 map 方法的实现不需要关心结果的类型:构建器会处理它。
另一方面,这意味着map 需要以某种方式接收此构建器。设计 Scala 2.8 Collections 时面临的问题是如何选择最好的构建器。例如,如果我要写Map('a' -> 1).map(_.swap),我希望得到一个Map(1 -> 'a') 回复。另一方面,Map('a' -> 1).map(_._1) 不能返回 Map(它返回 Iterable)。
通过这个CanBuildFrom 隐式执行从已知类型的表达式中产生最佳Builder 的魔力。
关于CanBuildFrom
为了更好地解释发生了什么,我将举一个示例,其中被映射的集合是 Map 而不是 List。我稍后会回到List。现在,考虑以下两个表达式:
Map(1 -> "one", 2 -> "two") map Function.tupled(_ -> _.length)
Map(1 -> "one", 2 -> "two") map (_._2)
第一个返回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。
回到问题
让我们看看问题的代码,List's 和map's 的定义(再次)看看类型是如何推断的:
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],所以TraversableLike上定义的A和Repr的类型是:
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]] 的类型或子类型。