我希望能够对T[_] <: Traversable
之类的类型进行一般性的处理,以便能够执行诸如map和filter之类的事情,但是我想推迟选择哪个Traversable
的决定。尽可能长的时间。
我希望能够针对返回T[Int]
而不是T[Int]
的通用Traversable[Int]
编写函数。因此,例如,我想将函数应用于Set[Int]
或Vector[Int]
或任何扩展Traversable并返回该类型的东西。
我首先尝试以一种简单的方式做到这一点:
trait CollectionHolder[T[_] <: Traversable[_]] {
def easyLessThanTen(xs: T[Int]): T[Int] = {
xs.filter(_ < 10)
}
}
但这无法编译:扩展功能缺少参数类型。但是,如果函数使用Traversable[Int]
而不是T[Int]
,它将进行编译,因此以为我可以使用Traversable
并将其转换为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)
我收到以下编译器错误:
无法使用元素类型构造Set [Int]类型的集合 Int基于Traversable [Int]类型的集合。不够 方法lessThanTen的参数:(隐式cbf: scala.collection.generic.CanBuildFrom [Traversable [Int],Int,Set [Int]] Set [Int]。 未指定值参数cbf。
在哪里可以获取CanBuildFrom进行转换?或者更好的是,如何修改所需结果的简单方法?还是我需要使用一个类型类为每个我感兴趣的Traversable(一个用于Set,一个用于Vector等)编写隐式实现?如果可能的话,我宁愿避免使用最后一种方法。
答案 0 :(得分:2)
使用(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]
,则不会违反第一个约束条件。
答案 1 :(得分:1)
是的,我是说您应该使用 typeclasses 。
但是,您不必实现它们,也不必提供所需类型的实例。因此,它们非常常见,可以在库中找到,例如cats
或scalaz
。
例如,使用cats
:
import cats.{Traverse, TraverseFilter}
import cats.syntax.all._ // Provides the nonEmpty, filter & map extension methods to C.
import scala.language.higherKinds
def algorithm[C[_]: TraverseFilter: Traverse](col: C[Int]): C[Int] =
if (col.nonEmpty)
col.filter(x => x < 10)
else
col.map(x => x * 2) // nonsense, but just to show that you can use map too.
您可以这样使用:
import cats.instances.list._
algorithm(List(1, 200, 3, 100))
// res: List[Int] = List(1, 3)
可能值得补充的是,还有许多其他方法,例如exists
,foldLeft
,size
等。
看一下documentation。而且,如果这是您第一次使用cats
或scalaz
或一般的概念,您可能会发现scala-with-cats很有启发性。