Return 指定类型的通用 Traversable
Return a generic Traversable of a specified type
我希望能够像 T[_] <: Traversable
这样通用地操作类型,这样我就可以做映射和过滤之类的事情,但是我想推迟决定 Traversable
我select 尽可能长。
我希望能够针对 return 是 T[Int]
而不是 Traversable[Int]
的通用 T[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)
我收到以下编译器错误:
Cannot construct a collection of type Set[Int] with elements of type
Int based on a collection of type Traversable[Int]. not enough
arguments for method lessThanTen: (implicit cbf:
scala.collection.generic.CanBuildFrom[Traversable[Int],Int,Set[Int]])Set[Int].
Unspecified value parameter cbf.
从哪里可以得到 CanBuildFrom 来进行这种转换?或者更好的是,我如何修改更简单的方法以获得我想要的结果?或者我是否需要使用类型类并为我有兴趣使用的每个 Traversable 编写一个隐式实现(一个用于 Set,一个用于 Vector 等)?如果可能的话,我宁愿避免使用最后一种方法。
是的,我是说你应该使用 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. And if it is your first time using either cats
or scalaz
or those concepts in general, you may find scala-with-cats很有指导意义。
使用 (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
也是,问题是returns GenTraversable[A]
而不是T[A]
.
Scala 2.13 重新设计了集合,所以那里的方法可能会略有不同,但我还没有看够。
此外:与 T[A] <: Traversable[A]
相比,T[_] <: Traversable[_]
可能不是您想要的;例如如果您有 T[Int] <: Traversable[String]
.
,则不会违反第一个约束
我希望能够像 T[_] <: Traversable
这样通用地操作类型,这样我就可以做映射和过滤之类的事情,但是我想推迟决定 Traversable
我select 尽可能长。
我希望能够针对 return 是 T[Int]
而不是 Traversable[Int]
的通用 T[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)
我收到以下编译器错误:
Cannot construct a collection of type Set[Int] with elements of type Int based on a collection of type Traversable[Int]. not enough arguments for method lessThanTen: (implicit cbf: scala.collection.generic.CanBuildFrom[Traversable[Int],Int,Set[Int]])Set[Int]. Unspecified value parameter cbf.
从哪里可以得到 CanBuildFrom 来进行这种转换?或者更好的是,我如何修改更简单的方法以获得我想要的结果?或者我是否需要使用类型类并为我有兴趣使用的每个 Traversable 编写一个隐式实现(一个用于 Set,一个用于 Vector 等)?如果可能的话,我宁愿避免使用最后一种方法。
是的,我是说你应该使用 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. And if it is your first time using either cats
or scalaz
or those concepts in general, you may find scala-with-cats很有指导意义。
使用 (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
也是,问题是returns GenTraversable[A]
而不是T[A]
.
Scala 2.13 重新设计了集合,所以那里的方法可能会略有不同,但我还没有看够。
此外:与 T[A] <: Traversable[A]
相比,T[_] <: Traversable[_]
可能不是您想要的;例如如果您有 T[Int] <: Traversable[String]
.