简体   繁体   English

与Scala泛型一起使用Iterable

[英]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中,此模式由可遍历集合(例如SeqOption )和应用函子(例如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 . 请注意,我使用ListVector而不是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具体实现(例如ListVector )实现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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM