Scala 当选择用于CanBuildFrom函数的隐式时,优先规则是什么

Scala 当选择用于CanBuildFrom函数的隐式时,优先规则是什么,scala,collections,implicit,Scala,Collections,Implicit,由于缺少更好的示例,假设我定义了一个容器类型,该类型使用单个类型参数。假设此容器包装了相同类型的列表。我想在我的新容器上定义一个方法,这样每当执行一个操作时,它都会将调用委托给嵌入列表,但它会返回我的容器类型,这可能是由于一个不同的类型参数。为了实现这一点,我将使用scala集合中的隐式构建器模式。以下是基本结构: class Foo[A](val data: List[A]) { def foo[C, That](pf: PartialFunction[A, C])( implic

由于缺少更好的示例,假设我定义了一个容器类型,该类型使用单个类型参数。假设此容器包装了相同类型的列表。我想在我的新容器上定义一个方法,这样每当执行一个操作时,它都会将调用委托给嵌入列表,但它会返回我的容器类型,这可能是由于一个不同的类型参数。为了实现这一点,我将使用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,该CanBuildFrom接受一个'from'类型的Foo[\ux],一个'a'元素类型,并转换为'to'类型的'Foo[a]'。我想做的是获取一个'from'类型的List[\ux],一个'a'元素类型,然后转换为'Foo[a]的'to'类型。大致如下:

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,并且在本例中,范围中有一个隐式fn与输入参数类型列表匹配,那么这将是最佳匹配。是我做错了什么,还是出于安全原因,“from”集合在选择转换为哪个集合时具有最高优先级。我能做些什么来提高隐式的优先级吗?

它使用第一个匹配的。由于作用域中已经有一个canBuildFrom与canBuildFrom[List[\uuz],C匹配,所以它使用这个。您可以通过键入以下内容来查看:

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

谢谢你的评论。是的,我在发布之前检查了实际上在范围内的隐式。问题是我不想指定类型,我希望使用类型推断来完成这项工作。我想我的隐式应该优先,但我想在它进入方法foo之前不会这样。我能想到的唯一解决方案是将“That”替换为“Foo[C]”,因此“to”类型是显式的。这是可行的,但问题是,如果我不支持带有“Foo”的“C”类型,我希望它按照CanBuildFrom模式使用更通用的类型。那不行。