我希望有一个函数可以将C[_]
的任何可迭代类型Either[A, B]
转换为Either[C[A], C[B]]
。
我可以使用它,但是我使用了asInstanceOf
方法,我觉得这种方法在某些情况下可能会失败(由于我不太了解{{1} }解决)。
我认为我应该使用自定义CanBuildFrom
来实现它,但是我希望有更简单的方法来实现它。
这是我的方法:
CanBuildFrom
我已经在Scala中进行了一段时间编程,但是从不依赖type IterableCollection[A[_], B] = A[B] with Iterable[B]
implicit class IterableEither[C[_], A, B](self: IterableCollection[C, Either[A, B]]) {
def accumulate: Either[IterableCollection[C, A], IterableCollection[C, B]] = {
val failures = self.collect { case x @ Left(_) => x.value }.asInstanceOf[IterableCollection[C, A]]
if (failures.nonEmpty) Left(failures)
else Right(self.collect { case x @ Right(_) => x.value }.asInstanceOf[IterableCollection[C, B]])
}
}
,因此我有点害怕将这种代码引入生产环境。你们看到没有铸模的方法吗?
答案 0 :(得分:3)
对于猫来说,它将是一个遍历Validated并返回Either的函数。转换为Validated的原因是序列需要Applicative实例。
import cats.Traverse
import cats.data.{NonEmptyList, ValidatedNel}
import cats.implicits._
def accSequence[T[_], A, B](tab: T[Either[A, B]])(implicit T: Traverse[T]): Either[NonEmptyList[A], T[B]] =
tab.traverse[ValidatedNel[A, ?], B](_.toValidatedNel).toEither
val result: Either[NonEmptyList[Int], List[String]] = accSequence(List(Left(1), Right("A"), Left(2)))
答案 1 :(得分:1)
这是一个不依赖asInstanceOf
的合理建议:
import scala.language.higherKinds
object EitherAccumulatorExample {
import scala.collection.{IterableLike, TraversableOnce}
import scala.collection.generic.CanBuildFrom
implicit class EitherAccumulator[C[X] <: IterableLike[X, C[X]], A, B](wrapped: C[Either[A, B]]) {
def accumulate[CA <: TraversableOnce[_], CB](
implicit
cbfA: CanBuildFrom[C[Either[A, B]], A, CA],
cbfB: CanBuildFrom[C[Either[A, B]], B, CB]
): Either[CA, CB] = {
val failures: CA = wrapped.collect{ case x @ Left(_) => x.value }(cbfA)
if (failures.nonEmpty) Left(failures)
else {
val successes: CB = wrapped.collect { case x @ Right(_) => x.value }(cbfB)
Right(successes)
}
}
}
val example = List.empty[Either[Int, Char]]
val foo: Either[List[Int], List[Char]] = example.accumulate
val example2 = Vector.empty[Either[String, Double]]
val bar: Either[Vector[String], Vector[Double]] = example2.accumulate
}
我个人宁愿建议查看Validated
,它应该有一个Traverse
实例,该实例又提供了一个sequence
方法,也许这就是您想要的。
答案 2 :(得分:1)
我以为我会和猫一起尝试这个,并想出了这个办法:
object SO {
import cats.instances.either._
import cats.instances.list._
import cats.syntax.traverse._
// type alias for the Iterable C
type C[A] = Iterable[A]
def doIt[A, B](c: C[Either[A, B]]): Either[A, C[B]] =
c.toList.sequence[Either[A, ?], B].map(_.to[C])
}
请注意,我使用的是Kind Projector,因此是“?”