将 Iterable 与 scala 泛型一起使用



我有两个具有完全相同实现的函数 - 只有一个处理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 中,此模式由可遍历集合(如 SeqOption(和应用函子(如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

请注意,我使用了ListVector而不是Seq。这是因为scalaz没有为 Seq 提供隐式Traverse实例。虽然从概念上讲Seq是可遍历的,但最好(出于性能原因(专门为Seq(如ListVector(的具体实现实现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

最新更新