选择隐含用于CanBuildFrom函数的优先级规则是什么

时间:2012-04-01 02:07:56

标签: scala collections implicit

由于缺少一个更好的例子,假设我有一个定义的容器类型,它接受一个类型参数。假设此容器包装了相同类型的List。我想在我的新容器上定义一个方法,这样无论何时执行一个操作,它都会将调用委托给嵌入列表,但它会返回我的容器类型(可能是一个不同的类型参数)。为此,我将使用scala集合中的隐式构建器模式。这是基本结构:

class Foo[A](val data: List[A]) {
  def foo[C, That](pf: PartialFunction[A, C])(
    implicit bf: CanBuildFrom[Foo[_], C, That]
  ): That = {
    bf(new Foo(data.collect(pf))).result
  } 
}

object Foo {
  def newBuilder[A]: Builder[A, Foo[A]] =
    new ArrayBuffer[A] mapResult { r => new Foo(r.toList) }

  implicit def canBuildFrom[A]: CanBuildFrom[Foo[_], A, Foo[A]] =
    new CanBuildFrom[Foo[_], A, Foo[A]] {
      def apply(from: Foo[_]) = newBuilder 
      def apply() = newBuilder 
    }
}

因此,当我的pf从Int转换为String时,我希望返回一个Foo [String]:

scala> val f = new Foo(List(1,2,3))
f: Foo[Int] = Foo@15172301

scala> f.foo { case x => x.toString }
res318: Foo[java.lang.String] = Foo@6ff763fa

虽然前面的例子是基于CanBuildFrom采用'from'类型的Foo [_],元素类型为'A',并转换为'to'类型的'Foo [A]'。我想做的是采用一种'from'类型的List [_],一种元素类型'A',并转换为'to'类型的'Foo [A]'。这些方面的东西:

class Foo[A](val data: List[A]) {
  def foo[C, That](pf: PartialFunction[A, C])(
    implicit bf: CanBuildFrom[List[_], C, That]
  ): That = {
    data.collect(pf)(bf)
  } 
}

object Foo {
  def newBuilder[A]: Builder[A, Foo[A]] =
    new ArrayBuffer[A] mapResult { r => new Foo(r.toList) }

  implicit def canBuildFrom[A]: CanBuildFrom[List[_], A, Foo[A]] =
    new CanBuildFrom[List[_], A, Foo[A]] {
      def apply(from: List[_]) = newBuilder 
      def apply() = newBuilder 
    }
}

这里我将隐式的CanBuildFrom参数直接传递给List类,以便它可以构建我的Foo类来存储结果。但是,当我运行相同的测试时,而不是获得Foo [String],我获取List [String]。换句话说,它不使用我的隐含,它使用的是通用版本。

scala> val f = new Foo(List(1,2,3))
f: Foo[Int] = Foo@236f7a59

scala> f.foo { case x => x.toString }
res319: List[java.lang.String] = List(1, 2, 3)

所以,我的问题是为什么?我原以为如果我当前的类型是Foo并且我正在转换为Foo并且在输入参数类型(在这种情况下为List)中匹配范围内存在隐式fn,那么这将是最佳匹配。我做错了什么,或者出于安全原因,'from'集合在选择转换为哪个集合时具有最优先权。我能做些什么来提高我隐含的优先权吗?

1 个答案:

答案 0 :(得分:0)

它使用匹配的第一个。因为在CanBuildFrom [List [_],C,That]匹配的范围内已经有一个canBuildFrom,它使用它。您可以通过输入以下内容来查看:

implicitly[CanBuildFrom[List[_], _, _]]
// => res3: scala.collection.generic.CanBuildFrom[List[_], _, Any] = scala.collection.generic.GenTraversableFactory$GenericCanBuildFrom@6a3a191e

但是,您可以强制编译器通过指定存储结果的变量的类型来搜索返回Foo的编译器:

val y: Foo[String] = x.foo { case x => x.toString }
// => y: Foo[String] = Foo@76faf7d6