将 Iterable 与 Scala 泛型一起使用
Using Iterable with scala generics
我有两个实现完全相同的函数 - 只有一个处理 Option
,另一个处理 Seq
。我想使用泛型将其编写为处理 Iterable
的单个函数,同时在调用代码中保留具体类型 - 如果可能的话?
def f[T](a: Seq[Failure \/ T]): Failure \/ Seq[T] = { ??? }
def g[T](b: Option[Failure \/ T]): Failure \/ Option[T] = { ??? }
实现并不重要,但对于上下文,它们从结果集合(每个结果可能成功 (T
) 或失败 (Failure
))转换为单一失败或成功结果的完整集合。 \/
只是 scalaz 的 Either 版本。
我想做这样的事情:
def f[I[T] <: Iterable[T]](results: I[Failure \/ T]): Failure \/ I[T] = { ??? }
您可以这样做(以 Future.sequence
的实现为例):
def f[T, M[X] <: Iterable[X]](results: M[Failure \/ T): Failure \/ M[T]
您可能还需要一些 CanBuildFrom
。
我脑子里乱七八糟的,没有测试,所以对打字错误表示歉意。
import scala.collection.generic.CanBuildFrom
def combine[M[X] <: Iterable[X], T](
input: M[Failure \/ T]
)(
implicit cbf: CanBuildFrom[Nothing, T, M[T]]
): Failure \/ M[T] = {
def inner(builder: Builder[T, M[T]], els: M[T]): Failure \/ M[T] = {
els.headOption match {
case Some(\/-(right)) => inner(builder += right, els.tail)
case Some(-\/(left)) => -\/(left)
case None => \/-(builder.result())
}
}
inner(cbf(), input)
}
类似的东西,你有一个内部递归 "short circuits" 当发现第一个失败时。
在 FP 中,此模式由 traversable collection (such as Seq
or Option
) and an applicative 函子(例如 Failure \/ ?
)之间的相互作用表示。
通用实现(使用scalaz
)是
import scalaz._
import scalaz.syntax.traverse._
def f[F[_]: Traverse, G[_]: Applicative, T](a: F[G[T]]): G[F[T]] = a.sequence
在调用站点,你会做
import scalaz.std._
type FailureOr[A] = Failure \/ A
val x: Option[FailureOr[Int]] = ???
val y: List[FailureOr[Int]] = ???
val z: Vector[FailureOr[Int]] = ???
f[Option, FailureOr, Int](x)
f[List, FailureOr, Int](y)
f[Vector, FailureOr, Int](z)
// or just directly
import scalaz.syntax.traverse._
x.sequence
y.sequence
z.sequence
请注意,我使用了 List
和 Vector
而不是 Seq
。这是因为 scalaz
没有为 Seq
提供隐式的 Traverse
实例。虽然从概念上讲 Seq
是可遍历的,但最好(出于性能原因)实施 Traverse
操作专门用于 Seq
的具体实现,例如 List
或 Vector
.如果你真的想要,你可以编写自己的 Traverse[Seq]
实例,只需要知道它对于 Seq
.
的某些实现来说不是最优的
我有两个实现完全相同的函数 - 只有一个处理 Option
,另一个处理 Seq
。我想使用泛型将其编写为处理 Iterable
的单个函数,同时在调用代码中保留具体类型 - 如果可能的话?
def f[T](a: Seq[Failure \/ T]): Failure \/ Seq[T] = { ??? }
def g[T](b: Option[Failure \/ T]): Failure \/ Option[T] = { ??? }
实现并不重要,但对于上下文,它们从结果集合(每个结果可能成功 (T
) 或失败 (Failure
))转换为单一失败或成功结果的完整集合。 \/
只是 scalaz 的 Either 版本。
我想做这样的事情:
def f[I[T] <: Iterable[T]](results: I[Failure \/ T]): Failure \/ I[T] = { ??? }
您可以这样做(以 Future.sequence
的实现为例):
def f[T, M[X] <: Iterable[X]](results: M[Failure \/ T): Failure \/ M[T]
您可能还需要一些 CanBuildFrom
。
我脑子里乱七八糟的,没有测试,所以对打字错误表示歉意。
import scala.collection.generic.CanBuildFrom
def combine[M[X] <: Iterable[X], T](
input: M[Failure \/ T]
)(
implicit cbf: CanBuildFrom[Nothing, T, M[T]]
): Failure \/ M[T] = {
def inner(builder: Builder[T, M[T]], els: M[T]): Failure \/ M[T] = {
els.headOption match {
case Some(\/-(right)) => inner(builder += right, els.tail)
case Some(-\/(left)) => -\/(left)
case None => \/-(builder.result())
}
}
inner(cbf(), input)
}
类似的东西,你有一个内部递归 "short circuits" 当发现第一个失败时。
在 FP 中,此模式由 traversable collection (such as Seq
or Option
) and an applicative 函子(例如 Failure \/ ?
)之间的相互作用表示。
通用实现(使用scalaz
)是
import scalaz._
import scalaz.syntax.traverse._
def f[F[_]: Traverse, G[_]: Applicative, T](a: F[G[T]]): G[F[T]] = a.sequence
在调用站点,你会做
import scalaz.std._
type FailureOr[A] = Failure \/ A
val x: Option[FailureOr[Int]] = ???
val y: List[FailureOr[Int]] = ???
val z: Vector[FailureOr[Int]] = ???
f[Option, FailureOr, Int](x)
f[List, FailureOr, Int](y)
f[Vector, FailureOr, Int](z)
// or just directly
import scalaz.syntax.traverse._
x.sequence
y.sequence
z.sequence
请注意,我使用了 List
和 Vector
而不是 Seq
。这是因为 scalaz
没有为 Seq
提供隐式的 Traverse
实例。虽然从概念上讲 Seq
是可遍历的,但最好(出于性能原因)实施 Traverse
操作专门用于 Seq
的具体实现,例如 List
或 Vector
.如果你真的想要,你可以编写自己的 Traverse[Seq]
实例,只需要知道它对于 Seq
.