简体   繁体   English

返回指定类型的通用Traversable

[英]Return a generic Traversable of a specified type

I'd like to be able to generically manipulate types like T[_] <: Traversable so that I can do things like map and filter, but I'd like to defer the decision about which Traversable I select for as long as possible. 我希望能够通用地操纵T[_] <: Traversable类的类型,以便我可以执行诸如map和filter之类的事情,但是我想推迟尽可能长地选择哪种Traversable的决定。

I'd like to be able write functions against a generic T[Int] that return a T[Int] not a Traversable[Int] . 我希望能够针对返回T[Int]而不是Traversable[Int]的通用T[Int]编写函数。 So for example, I'd like to apply a function to a Set[Int] or a Vector[Int] or anything that extends Traversable and get that type back. 因此,例如,我想将一个函数应用于Set[Int]Vector[Int]或任何扩展Traversable并返回该类型的函数。

I first attempted to do this in a simple manner like: 我首先尝试以一种简单的方式执行此操作,例如:

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

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

but this won't compile: Missing parameter type for expanded function. 但这不会编译:扩展功能缺少参数类型。 It will compile, however, if the function takes a Traversable[Int] instead of a T[Int] , so thought I could work with Traversable and convert to a T . 但是,如果函数采用Traversable[Int]而不是T[Int] ,它将进行编译,因此以为我可以使用Traversable并将其转换为T This lead me to CanBuildFrom 这导致我转向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
}

which compiles. 编译。 But then in my tests: 但是然后在我的测试中:

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

object withSet extends CollectionHolder[Set]

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

I get the following compiler error: 我收到以下编译器错误:

Cannot construct a collection of type Set[Int] with elements of type Int based on a collection of type Traversable[Int]. 无法基于Traversable [Int]类型的集合构造具有Int类型元素的Set [Int]类型集合。 not enough arguments for method lessThanTen: (implicit cbf: scala.collection.generic.CanBuildFrom[Traversable[Int],Int,Set[Int]])Set[Int]. 方法lessThanTen没有足够的参数:(隐式cbf:scala.collection.generic.CanBuildFrom [Traversable [Int],Int,Set [Int]])Set [Int]。 Unspecified value parameter cbf. 未指定的值参数cbf。

Where can I get a CanBuildFrom to make this conversion? 在哪里可以获得CanBuildFrom进行转换? Or better yet, how can I modify my simpler approach for the result I want? 或者更好的是,如何修改所需结果的简单方法? Or do I need to use a typeclass and write an implicit implementation for each Traversable I'm interested in using (one for Set, one for Vector etc)? 还是我需要使用一个类型类为每个我感兴趣的Traversable(一个用于Set,一个用于Vector等)编写隐式实现? I'd prefer to avoid the last approach if possible. 如果可能的话,我宁愿避免使用最后一种方法。

Using the (Scala 2.12.8) standard library instead of cats/scalaz/etc. 使用(Scala 2.12.8)标准库而不是cats / scalaz / etc。 you need to look at GenericTraversableTemplate . 您需要查看GenericTraversableTemplate filter isn't defined there, but can easily be: 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()
  }
}

In the comment you mention nonEmpty and exists ; 在评论中,您提到nonEmpty并且exists they are available because of the GenTraversable type bound. 由于GenTraversable类型绑定,它们可用。 Really filter is too, the problem is that it returns GenTraversable[A] instead of T[A] . 真正的filter也是,问题在于它返回GenTraversable[A]而不是T[A]

Scala 2.13 reworks collections so the methods will probably be slightly different there, but I haven't looked enough at it yet. Scala 2.13重做了集合,因此那里的方法可能略有不同,但是我还没有足够地了解它。

Also: T[_] <: Traversable[_] is likely not what you want as opposed to T[A] <: Traversable[A] ; 另外: T[_] <: Traversable[_]可能不是您想要的,而不是T[A] <: Traversable[A] ; eg the first constraint is not violated if you have T[Int] <: Traversable[String] . 例如,如果您有T[Int] <: Traversable[String]则不违反第一个约束。

Yes, I am saying that you should use typeclasses . 是的,我是说您应该使用typeclasss
But, you do not have to implement them, nor provide their instances for the types you need. 但是,您不必实现它们,也不必提供所需类型的实例。 As, those are very common and can be found in libraries like cats or scalaz . 因为,这些非常常见,可以在catsscalaz类的库中找到。

For example, using cats : 例如,使用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.

Which you can use like this: 您可以这样使用:

import cats.instances.list._

algorithm(List(1, 200, 3, 100))
// res: List[Int] = List(1, 3)

It may be worth adding, that there are a lot of other methods like exists , foldLeft , size , etc. 这可能是值得加入,有很多其他的方法,如existsfoldLeftsize等。
Take a look to the documentation . 看一下文档 And if it is your first time using either cats or scalaz or those concepts in general, you may find scala-with-cats very instructive. 而且,如果这是您第一次使用catsscalaz或一般概念,那么您可能会发现scala-with-cats非常有启发性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM