在我问了另一个问题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绑定(bind)到B(即,它是B的某种集合)的事实。类型参数看起来既与上面的签名无关,又与特征CanBuildFrom本身的签名无关:
    trait CanBuildFrom[-From, -Elem, +To]
    

    Scala编译器如何确保不能将That强制转换为没有意义的内容?
    > val s: Set[String] = List("Paris", "London").map(_.length) //will not compile
    

    编译器如何确定在调用范围内包含哪些隐式CanBuildFrom对象?

    最佳答案

    请注意,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绑定(bind)了第二个和第三个参数,所以隐式的问题将不起作用。

    那么,关于这样的隐式,如何知道在哪里寻找呢?首先,Scala确实将一些东西导入了所有范围。现在,我可以回顾以下进口:
    import scala.package._ // Package object
    import scala.Predef._  // Object
    // import scala.LowPriorityImplicits, class inherited by Predef
    import scala.runtime._ // Package
    

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

    现在,CanBuildFromPredef内部有LowPriorityImplicits隐式对象,它们与字符串有关。它们使我们能够编写"this is a string" map (_.toInt)

    因此,除了这些自动导入和您进行的显式导入之外,还可以在哪里找到隐式?一个地方:在其上应用方法的实例的伴随对象。

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

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

    10-06 16:14