我有两个具有完全相同实现的函数 - 只有一个处理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版本的Both。
我想做这样的事情:
def f[I[T] <: Iterable[T]](results: I[Failure / T]): Failure / I[T] = { ??? }
在 FP 中,此模式由可遍历集合(如 Seq
或 Option
(和应用函子(如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
是可遍历的,但最好(出于性能原因(专门为Seq
(如List
或Vector
(的具体实现实现Traverse
操作。如果你真的愿意,你可以编写自己的Traverse[Seq]
实例,只要知道对于Seq
的某些实现来说,这将是次优的。
在我的头顶上,没有测试,所以对错别字表示歉意。
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)
}
像这样的事情,你有一个内部递归,当发现第一个故障时,它会"短路"。
做这样的事情(参见Future.sequence
实现作为示例(:
def f[T, M[X] <: Iterable[X]](results: M[Failure / T): Failure / M[T]
您可能还需要一些CanBuildFrom
。