【问题标题】:What are the rules for precedence when it comes to choosing an implicit to use for the CanBuildFrom function选择隐式用于 CanBuildFrom 函数时的优先级规则是什么
【发布时间】:2012-04-15 05:22:44
【问题描述】:

由于缺少更好的示例,假设我定义了一个容器类型,它采用单个类型参数。假设这个容器包装了一个相同类型的 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,它采用 Foo[_] 的“from”类型、“A”的元素类型,并转换为“Foo[A]”的“to”类型。我想做的是采用 List[_] 的 'from' 类型,'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],而是得到一个列表[字符串]。换句话说,它不是使用我的隐式,而是使用通用版本。

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 匹配输入参数类型(在本例中为 List ),那么这将是最佳匹配。我做错了什么还是出于安全原因,“来自”集合在选择转换为哪个集合时具有最高优先级。我可以做些什么来提高我的隐含优先级吗?

【问题讨论】:

    标签: scala collections implicit


    【解决方案1】:

    它使用第一个匹配的。由于在与 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
    

    【讨论】:

    • 感谢 cmets。是的,我在发布之前检查了实际上在范围内的隐式。问题是我不想指定类型,我希望类型推断来完成这项工作。我在想我的隐式应该优先,但我想在它进入方法 foo 之前不会是这种情况。我能想到的唯一解决方案是将 'That' 替换为 'Foo[C]' ,因此 'to' 类型是明确的。这行得通,但问题是,如果我不支持带有 'Foo' 的 'C' 类型,我希望它使用更通用的类型(根据 CanBuildFrom 模式)。那是行不通的。
    猜你喜欢
    • 1970-01-01
    • 2010-12-10
    • 1970-01-01
    • 2023-01-03
    • 1970-01-01
    • 2021-09-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多