scala - 返回指定类型的通用 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 等)?如果可能的话,我宁愿避免使用最后一种方法。
解决方案
使用(Scala 2.12.8)标准库而不是cats/scalaz/etc。你需要看看GenericTraversableTemplate
。filter
没有在那里定义,但很容易是:
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()
}
}
在您提到的评论中nonEmpty
和exists
;GenTraversable
由于类型绑定,它们可用。真的filter
也是,问题是它返回GenTraversable[A]
而不是T[A]
.
Scala 2.13 对集合进行了重新设计,因此那里的方法可能会略有不同,但我还没有看够。
另外:T[_] <: Traversable[_]
可能不是您想要的,而不是T[A] <: Traversable[A]
; 例如,如果您有,则不会违反第一个约束T[Int] <: Traversable[String]
。
推荐阅读
- image - 什么格式的图片?
- ios - iOS (Swift) - UIViewControllers 数组
- c++ - c++ 多播套接字读取挂起linux
- swift - SpriteKit - 如何制作透视滚动背景
- c# - 带有种子的添加迁移引发“未提供值”异常
- cassandra - 为什么墓碑可以依赖 `liveness_info` 时包含 `marked_deleted` 时间戳?
- r - 使用复选框 Leaflet/R 添加标记
- android - 无法解决依赖 firebase 和 google play 服务
- datagrip - 如何在 DataGrip 中禁用自动格式化
- docker - 部署到 Google Compute Engine 的 Docker 映像不断重启