27

继我提出的另一个问题Scala 2.8 breakout之后,我想进一步了解TraversableLike[A].map签名如下的 Scala 方法:

def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That

请注意有关此方法的一些事项:

  • 它需要一个函数将A可遍历中的每个转换为B.
  • 它返回That并接受 type 的隐式参数CanBuildFrom[Repr, B, That]

我可以这样称呼它:

> val s: Set[Int] = List("Paris", "London").map(_.length)
s: Set[Int] Set(5,6)

不能完全理解的是编译器如何强制执行绑定到的事实(That即,它是 B 的一些集合)。类型参数看起来独立于上面的签名和特征本身的签名:BCanBuildFrom

trait CanBuildFrom[-From, -Elem, +To]

Scala 编译器如何确保That不能强制执行一些没有意义的事情?

> val s: Set[String] = List("Paris", "London").map(_.length) //will not compile

编译器如何决定CanBuildFrom调用范围内的隐式对象?

4

2 回答 2

29

请注意,第二个参数 tomap是一个隐式参数。必须有一个具有适当类型的隐式作用域,否则,您必须传递这样的参数。

在您的示例中,That必须是Set[String],B 必须是Int并且Repr必须是List[String]。因此,要编译,您需要范围内的以下隐式对象:

implicit object X: CanBuildFrom[List[String], Int, Set[String]]

范围内没有这样的事情。此外,breakOut不能提供它,因为它本身需要一个隐式CanBuildFrom,其第一个类型可以是任何类( 的反变后代Nothing),但受其他类型的限制。

例如,看一下CanBuildFrom来自伴随对象的工厂List

implicit def  canBuildFrom [A] : CanBuildFrom[List, A, List[A]]  

因为它通过 绑定第二个和第三个参数A,所以有问题的隐式将不起作用。

那么,对于这样的隐含,人们怎么知道去哪里寻找呢?首先,Scala 确实将一些东西导入到所有作用域中。现在,我可以回想起以下导入:

import scala.package._ // Package object
import scala.Predef._  // Object
// import scala.LowPriorityImplicits, class inherited by Predef
import scala.runtime._ // Package

由于我们关心隐式,请注意,当您从包中导入内容时,唯一可能的隐式是单例。另一方面,当您从对象(单例)导入事物时,您可以有隐式定义、值和单例。

现在, and 内部有隐含CanBuildFrom,它们与字符串有关。它们使我们能够写作。PredefLowPriorityImplicits"this is a string" map (_.toInt)

那么,除了这些自动导入以及您所做的显式导入之外,还能在哪里找到隐式导入?一个地方:应用该方法的实例的伴随对象。

我说伴生对象s是复数形式,因为由相关实例的类继承的所有特征和类的伴生对象可能包含相关的隐含。我不确定实例本身是否可能包含隐式。老实说,我现在无法重现这个,所以我肯定在这里犯了某种错误。

无论如何,请查看伴随对象的内部。

于 2009-11-12T12:48:51.050 回答
0
object ArrayBuffer extends SeqFactory[ArrayBuffer] {
  /** $genericCanBuildFromInfo */
  implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, ArrayBuffer[A]] = new GenericCanBuildFrom[A]
  def newBuilder[A]: Builder[A, ArrayBuffer[A]] = new ArrayBuffer[A]
}
于 2012-06-03T22:37:36.323 回答