在 Scala 中对 collections 进行操作的通用函数
generic functions that operates on collections in scala
这似乎是一个简单的问题,我确信以前有人问过它,但找不到我要找的东西。
如何编写一个以 collection 作为参数(或任何可以被视为 collection 的参数)的函数,对其执行一些操作,然后 return一个collection同类型的?
例如:
scala> def foo[Repr <% Traversable[String]](repr: Repr) = repr.map(_.size)
foo: [Repr](repr: Repr)(implicit evidence: Repr => Traversable[String])Traversable[Int]
这在某些 collection 上工作正常:
scala> foo(Vector("Hello","World"))
res0: Traversable[Int] = Vector(5, 5)
但当我尝试其他 collections(例如 Option
)时感到惊讶:
scala> foo(Some("HelloWorld"))
res1: Traversable[Int] = List(10)
一个小问题是 return 类型 Traversable
,理想情况下,它是提供给方法的任何类型。更大的问题是实际的实现类型:Option
变成了 List
。
更糟糕的是,当在 类 上尝试时(其行为类似于 collections)但没有隐含在它们的范围内。例如:Try
:
scala> import scala.util._
import scala.util._
scala> foo(Success("HelloWorld"))
<console>:12: error: No implicit view available from scala.util.Success[String] => Traversable[String].
foo(Success("HelloWorld"))
^
那么,有没有一种方法可以编写一个通用函数,当给定一个“collection 类”参数时,可以对其元素和 return 正确的类型进行操作?
理想情况下,我想将它用在任何东西上(甚至 Future
和 Try
),但对于我的特定用途,我可以只使用真正的 collections & Option
.
编辑:
为了说明一个可能的解决方案(这迫使我复制和粘贴代码,因此,这不是我要找的)是简单地编写两个没有视图边界的函数:
scala> :paste
// Entering paste mode (ctrl-D to finish)
def foo[Repr <: Traversable[String]](repr: Repr) = repr.map(_.size)
def foo(repr: Option[String]) = repr.map(_.size)
// Exiting paste mode, now interpreting.
foo: [Repr <: Traversable[String]](repr: Repr)Traversable[Int] <and> (repr: Option[String])Option[Int]
foo: [Repr <: Traversable[String]](repr: Repr)Traversable[Int] <and> (repr: Option[String])Option[Int]
scala> foo(Vector("bar"))
res2: Traversable[Int] = Vector(3)
scala> foo(Some("bar"))
res3: Option[Int] = Some(3)
映射的概念用函子来表示。为 common 类 轻松提供仿函数实现的一种方法是使用 scalaz 库:
import scala.language.higherKinds
import scalaz.Functor
import scalaz.Scalaz._
def foo[El <: String, Coll[_]](repr: Coll[El])(implicit ev: Functor[Coll]) =
repr.map(_.size)
现在,这仅适用于 List
、Vector
和 Future
:
scala> foo(Vector("Hello","World"))
res1: scala.collection.immutable.Vector[Int] = Vector(5, 5)
scala> foo(List("Hello","World"))
res2: List[Int] = List(5, 5)
scala> import scala.concurrent.Future
scala> import scala.concurrent.ExecutionContext.Implicits.global
scala> foo(Future("HelloWorld")) andThen PartialFunction(println(_))
Success(10)
与 Some
一起使用有点问题,因为只有 Option
有 Functor
实现,而不是 Some
:
scala> foo(Some("HelloWorld"))
<console>:12: error: could not find implicit value for parameter ev: scalaz.Functor[Some]
foo(Some("HelloWorld"))
^
所以你必须提供 Option
而不是 Some
到 foo
:
scala> foo(Some("HelloWorld"): Option[String])
res3: Option[Int] = Some(10)
scala> foo(Option("HelloWorld"))
res4: Option[Int] = Some(10)
scala> foo("HelloWorld".some) // This is from scalaz
res5: Option[Int] = Some(10)
并且 scalaz 没有 Try
的任何类型类实现,因此如果您想将 Functor
与 Try
一起使用,您必须自己提供实现:
import scala.util.Try
import scalaz.Functor
implicit object TryIsFunctor extends Functor[Try] {
def map[A, B](fa: Try[A])(f: A => B): Try[B] = fa map f
}
那么 foo
将与 Try
一起工作,但类似于 Option
,参数的类型应该是 Try
,而不是 Success
或 Failure
:
scala> foo(Try("HelloWorld"))
res9: scala.util.Try[Int] = Success(10)
此外,我相信,在 scalaz 中没有 Functor
实现更通用的集合类型,例如 Iterable
或 Seq
。
在常见的高阶函数中Functor
只支持map
。所以要使用 flatMap
和 filter
你必须提供不同的类型 类 而不是 Functor
。例如scalaz.Monad
支持map
和flatMap
,scalaz.MonadPlus
支持map
、flatMap
和filter
。
如果您不想使用 scalaz,您可能必须自己制作一些与类型 类 非常相似的东西,以获得更好的结果类型而不是 Traversable
。例如,使用标准库中的 CanBuildFrom
。
我确实认为 Kolmar 对一般问题的看法是正确的,但 Scala 确实支持 duck-typing,因此您可以这样做:
def foo[T[V]](duck: {def map[U](value: String=>U): T[_]}) ={
duck.map(_.size)
}
foo(Vector("bar")).toVector //> res0: Vector[_] = List(3)
foo(Some("bar")) //> res1: Option[_] = Some(3)
(toVector 只是为了强制对否则产生的迭代器求值)
这似乎是一个简单的问题,我确信以前有人问过它,但找不到我要找的东西。
如何编写一个以 collection 作为参数(或任何可以被视为 collection 的参数)的函数,对其执行一些操作,然后 return一个collection同类型的?
例如:
scala> def foo[Repr <% Traversable[String]](repr: Repr) = repr.map(_.size)
foo: [Repr](repr: Repr)(implicit evidence: Repr => Traversable[String])Traversable[Int]
这在某些 collection 上工作正常:
scala> foo(Vector("Hello","World"))
res0: Traversable[Int] = Vector(5, 5)
但当我尝试其他 collections(例如 Option
)时感到惊讶:
scala> foo(Some("HelloWorld"))
res1: Traversable[Int] = List(10)
一个小问题是 return 类型 Traversable
,理想情况下,它是提供给方法的任何类型。更大的问题是实际的实现类型:Option
变成了 List
。
更糟糕的是,当在 类 上尝试时(其行为类似于 collections)但没有隐含在它们的范围内。例如:Try
:
scala> import scala.util._
import scala.util._
scala> foo(Success("HelloWorld"))
<console>:12: error: No implicit view available from scala.util.Success[String] => Traversable[String].
foo(Success("HelloWorld"))
^
那么,有没有一种方法可以编写一个通用函数,当给定一个“collection 类”参数时,可以对其元素和 return 正确的类型进行操作?
理想情况下,我想将它用在任何东西上(甚至 Future
和 Try
),但对于我的特定用途,我可以只使用真正的 collections & Option
.
编辑:
为了说明一个可能的解决方案(这迫使我复制和粘贴代码,因此,这不是我要找的)是简单地编写两个没有视图边界的函数:
scala> :paste
// Entering paste mode (ctrl-D to finish)
def foo[Repr <: Traversable[String]](repr: Repr) = repr.map(_.size)
def foo(repr: Option[String]) = repr.map(_.size)
// Exiting paste mode, now interpreting.
foo: [Repr <: Traversable[String]](repr: Repr)Traversable[Int] <and> (repr: Option[String])Option[Int]
foo: [Repr <: Traversable[String]](repr: Repr)Traversable[Int] <and> (repr: Option[String])Option[Int]
scala> foo(Vector("bar"))
res2: Traversable[Int] = Vector(3)
scala> foo(Some("bar"))
res3: Option[Int] = Some(3)
映射的概念用函子来表示。为 common 类 轻松提供仿函数实现的一种方法是使用 scalaz 库:
import scala.language.higherKinds
import scalaz.Functor
import scalaz.Scalaz._
def foo[El <: String, Coll[_]](repr: Coll[El])(implicit ev: Functor[Coll]) =
repr.map(_.size)
现在,这仅适用于 List
、Vector
和 Future
:
scala> foo(Vector("Hello","World"))
res1: scala.collection.immutable.Vector[Int] = Vector(5, 5)
scala> foo(List("Hello","World"))
res2: List[Int] = List(5, 5)
scala> import scala.concurrent.Future
scala> import scala.concurrent.ExecutionContext.Implicits.global
scala> foo(Future("HelloWorld")) andThen PartialFunction(println(_))
Success(10)
与 Some
一起使用有点问题,因为只有 Option
有 Functor
实现,而不是 Some
:
scala> foo(Some("HelloWorld"))
<console>:12: error: could not find implicit value for parameter ev: scalaz.Functor[Some]
foo(Some("HelloWorld"))
^
所以你必须提供 Option
而不是 Some
到 foo
:
scala> foo(Some("HelloWorld"): Option[String])
res3: Option[Int] = Some(10)
scala> foo(Option("HelloWorld"))
res4: Option[Int] = Some(10)
scala> foo("HelloWorld".some) // This is from scalaz
res5: Option[Int] = Some(10)
并且 scalaz 没有 Try
的任何类型类实现,因此如果您想将 Functor
与 Try
一起使用,您必须自己提供实现:
import scala.util.Try
import scalaz.Functor
implicit object TryIsFunctor extends Functor[Try] {
def map[A, B](fa: Try[A])(f: A => B): Try[B] = fa map f
}
那么 foo
将与 Try
一起工作,但类似于 Option
,参数的类型应该是 Try
,而不是 Success
或 Failure
:
scala> foo(Try("HelloWorld"))
res9: scala.util.Try[Int] = Success(10)
此外,我相信,在 scalaz 中没有 Functor
实现更通用的集合类型,例如 Iterable
或 Seq
。
在常见的高阶函数中Functor
只支持map
。所以要使用 flatMap
和 filter
你必须提供不同的类型 类 而不是 Functor
。例如scalaz.Monad
支持map
和flatMap
,scalaz.MonadPlus
支持map
、flatMap
和filter
。
如果您不想使用 scalaz,您可能必须自己制作一些与类型 类 非常相似的东西,以获得更好的结果类型而不是 Traversable
。例如,使用标准库中的 CanBuildFrom
。
我确实认为 Kolmar 对一般问题的看法是正确的,但 Scala 确实支持 duck-typing,因此您可以这样做:
def foo[T[V]](duck: {def map[U](value: String=>U): T[_]}) ={
duck.map(_.size)
}
foo(Vector("bar")).toVector //> res0: Vector[_] = List(3)
foo(Some("bar")) //> res1: Option[_] = Some(3)
(toVector 只是为了强制对否则产生的迭代器求值)