保留类型的任一序列的通用序列

时间:2018-07-22 18:16:42

标签: scala scala-collections either

我希望有一个函数可以将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]]) } } ,因此我有点害怕将这种代码引入生产环境。你们看到没有铸模的方法吗?

3 个答案:

答案 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,因此是“?”