Scala 2.8 CanBuildFrom

时间:2009-11-12 10:38:23

标签: generics scala scala-2.8

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

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

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

  • 需要一个函数将遍历中的每个A转换为B
  • 返回That并采用类型为CanBuildFrom[Repr, B, That]的隐式参数。

我可以这样称呼:

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

我无法理解That 绑定B的事实(也就是说,它是B的一些集合)正由编译器强制执行。类型参数看起来独立于上面的签名和特征CanBuildFrom本身的签名:

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

Scala编译器如何确保That不能强制进入无意义的事情?

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

编译器如何确定调用范围内的隐式CanBuildFrom对象是什么?

2 个答案:

答案 0 :(得分:29)

请注意,map的第二个参数是隐式参数。 必须是具有适当类型的隐含范围,否则,您必须传递此类参数。

在您的示例中,That必须为Set[String],B必须为IntRepr必须为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

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

目前,CanBuildFromPredef内有LowPriorityImplicits个含义,与字符串有关。它们使我们能够写"this is a string" map (_.toInt)

那么,除非这些自动导入和你做的显式导入,否则可以隐藏其他地方?一个地方:正在应用该方法的实例的伴随对象。

我用复数表示伴随对象 s ,因为所讨论的实例的类继承的所有特征和类的伴随对象可能包含相关的含义。我不确定实例本身是否包含隐式。老实说,我现在无法重现这一点,所以我肯定在这里犯了一些错误。

无论如何,请查看配套对象。

答案 1 :(得分: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]
}