将 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

请注意,我使用了 ListVector 而不是 Seq。这是因为 scalaz 没有为 Seq 提供隐式的 Traverse 实例。虽然从概念上讲 Seq 是可遍历的,但最好(出于性能原因)实施 Traverse 操作专门用于 Seq 的具体实现,例如 ListVector .如果你真的想要,你可以编写自己的 Traverse[Seq] 实例,只需要知道它对于 Seq.

的某些实现来说不是最优的