[英]Using Iterable with scala generics
I have two functions with the exact same implementation - only one handles Option
and the other handles Seq
. 我有两个函数具有完全相同的实现-只有一个处理
Option
,另一个处理Seq
。 I'd like to use generics to write this as a single function that handles Iterable
, while keeping the concrete type in the calling code - if this is possible? 我想使用泛型将其编写为处理
Iterable
的单个函数,同时将具体类型保留在调用代码中-如果可能的话?
def f[T](a: Seq[Failure \\/ T]): Failure \\/ Seq[T] = { ??? }
def g[T](b: Option[Failure \\/ T]): Failure \\/ Option[T] = { ??? }
The implementation isn't important, but for context they translate from a collection of results (each of which may either have succeeded ( T
) or failed ( Failure
)) to either a single failure or a complete collection of successful results. 实现并不重要,但是对于上下文而言,它们从结果的集合(每个结果可能是成功(
T
)或失败( Failure
))转换为单个失败或成功结果的完整集合。 \\/
is just scalaz's version of Either. \\/
只是scalaz的Either版本。
I'm looking to do something like this: 我正在寻找做这样的事情:
def f[I[T] <: Iterable[T]](results: I[Failure \\/ T]): Failure \\/ I[T] = { ??? }
In FP, this pattern is expressed by an interplay between a traversable collection (such as Seq
or Option
) and an applicative functor (such as Failure \\/ ?
). 在FP中,此模式由可遍历集合(例如
Seq
或Option
)和应用函子(例如Failure \\/ ?
)之间的相互作用表示。
The generic implementation (using scalaz
) is then 然后是通用实现(使用
scalaz
)
import scalaz._
import scalaz.syntax.traverse._
def f[F[_]: Traverse, G[_]: Applicative, T](a: F[G[T]]): G[F[T]] = a.sequence
At the call site, you would do 在呼叫站点,您可以
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
Note that I used List
and Vector
instead of Seq
. 请注意,我使用
List
和Vector
而不是Seq
。 This is because scalaz
doesn't provide an implicit Traverse
instance for Seq
. 这是因为
scalaz
没有为Seq
提供隐式的Traverse
实例。 While conceptually Seq
is traversable, it is better (for performance reasons) to implement the Traverse
operations specifically for concrete implementations of Seq
such as List
or Vector
. 虽然从概念上讲
Seq
是可遍历的,但出于性能原因,最好专门针对Seq
具体实现(例如List
或Vector
)实现Traverse
操作。 If you really want to, you can write your own instance of Traverse[Seq]
, just know that it will be suboptimal for some implementations of Seq
. 如果确实愿意,您可以编写自己的
Traverse[Seq]
实例,只知道它对于Seq
某些实现将不是最佳的。
Off the top of my head and not tested, so apologies for typos. 头顶上没有经过测试,所以对错别字表示歉意。
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)
}
Something like that, you have an inner recursion that "short circuits" when the first failure is found. 像这样,您有一个内部递归,当发现第一个故障时,它会“短路”。
You can do something like this (see implementation for Future.sequence
as an example): 您可以执行以下操作(请参阅
Future.sequence
实现作为示例):
def f[T, M[X] <: Iterable[X]](results: M[Failure \/ T): Failure \/ M[T]
You'll probably need some CanBuildFrom
too. 您可能还需要一些
CanBuildFrom
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.