首页 > 解决方案 > 返回指定类型的通用 Traversable

问题描述

我希望能够像T[_] <: Traversable这样一般地操作类型,以便我可以执行映射和过滤器之类的操作,但我想Traversable尽可能长时间地推迟我选择的决定。

我希望能够针对T[Int]返回 aT[Int]而不是 a的泛型编写函数Traversable[Int]。因此,例如,我想将一个函数应用于 aSet[Int]或 aVector[Int]或任何扩展 Traversable 并取回该类型的东西。

我首先尝试以一种简单的方式执行此操作,例如:

trait CollectionHolder[T[_] <: Traversable[_]] {

  def easyLessThanTen(xs: T[Int]): T[Int] = {
    xs.filter(_ < 10)
  }
}

但这不会编译:缺少扩展函数的参数类型。Traversable[Int]但是,如果函数采用 a而不是 a ,它将编译,T[Int]所以我认为我可以使用Traversable并转换为 a T。这导致我CanBuildFrom

object DoingThingsWithTypes {    

  trait CollectionHolder[T[_] <: Traversable[_]] {

    def lessThanTen(xs: T[Int])(implicit cbf: CanBuildFrom[Traversable[Int], Int, T[Int]]): T[Int] = {

      val filteredTraversable = xs.asInstanceOf[Traversable[Int]].filter(_ < 10)

      (cbf() ++= filteredTraversable).result
}

编译。但后来在我的测试中:

val xs = Set(1, 2, 3, 4, 1000)

object withSet extends CollectionHolder[Set]

withSet.lessThanTen(xs) shouldBe Set(1, 2, 3, 4)

我收到以下编译器错误:

无法基于 Traversable[Int] 类型的集合构造具有 Int 类型元素的 Set[Int] 类型集合。方法lessThanTen的参数不足:(隐式cbf:scala.collection.generic.CanBuildFrom[Traversable[Int],Int,Set[Int]])Set[Int]。未指定的值参数 cbf。

我在哪里可以获得 CanBuildFrom 来进行此转换?或者更好的是,我怎样才能修改我更简单的方法以获得我想要的结果?或者我是否需要使用类型类并为我感兴趣的每个 Traversable 编写一个隐式实现(一个用于 Set,一个用于 Vector 等)?如果可能的话,我宁愿避免使用最后一种方法。

标签: scalagenericsscala-collectionstype-parameter

解决方案


使用(Scala 2.12.8)标准库而不是cats/scalaz/etc。你需要看看GenericTraversableTemplatefilter没有在那里定义,但很容易是:

import scala.collection.GenTraversable
import scala.collection.generic.GenericTraversableTemplate

trait CollectionHolder[T[A] <: GenTraversable[A] with GenericTraversableTemplate[A, T]] {

  def lessThanTen(xs: T[Int]): T[Int] = {
    filter(xs)(_ < 10)
  }

  def filter[A](xs: T[A])(pred: A => Boolean) = {
    val builder = xs.genericBuilder[A]
    xs.foreach(x => if (pred(x)) { builder += x })
    builder.result()
  }
}

在您提到的评论中nonEmptyexistsGenTraversable由于类型绑定,它们可用。真的filter也是,问题是它返回GenTraversable[A]而不是T[A].

Scala 2.13 对集合进行了重新设计,因此那里的方法可能会略有不同,但我还没有看够。

另外:T[_] <: Traversable[_]可能不是您想要的,而不是T[A] <: Traversable[A]; 例如,如果您有,则不会违反第一个约束T[Int] <: Traversable[String]


推荐阅读