![](/img/trans.png)
[英]Type Mismatch on Recursive Call of Method with Generic Traversable Parameters in Scala
[英]Return a generic Traversable of a specified type
我希望能夠通用地操縱T[_] <: Traversable
類的類型,以便我可以執行諸如map和filter之類的事情,但是我想推遲盡可能長地選擇哪種Traversable
的決定。
我希望能夠針對返回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)
我收到以下編譯器錯誤:
無法基於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]
則不違反第一個約束。
是的,我是說您應該使用typeclasss 。
但是,您不必實現它們,也不必提供所需類型的實例。 因為,這些非常常見,可以在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
等。
看一下文檔 。 而且,如果這是您第一次使用cats
, scalaz
或一般概念,那么您可能會發現scala-with-cats非常有啟發性。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.