Scala 当选择用于CanBuildFrom函数的隐式时,优先规则是什么
由于缺少更好的示例,假设我定义了一个容器类型,该类型使用单个类型参数。假设此容器包装了相同类型的列表。我想在我的新容器上定义一个方法,这样每当执行一个操作时,它都会将调用委托给嵌入列表,但它会返回我的容器类型,这可能是由于一个不同的类型参数。为了实现这一点,我将使用scala集合中的隐式构建器模式。以下是基本结构: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
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模式使用更通用的类型。那不行。