具有保留类型的序列的通用序列
Generic sequence of Either to Either of sequence with preserved type
我想要一个函数,可以将 Either[A, B]
的任何可迭代类型 C[_]
转换为 Either[C[A], C[B]]
。
我成功了,但我使用了 asInstanceOf
方法,我觉得这种方法在某些情况下可能会失败(我还不知道那会是什么场景,因为我不太明白 CanBuildFrom
解决).
我认为我应该使用自定义实现它 CanBuildFrom
但我希望有更简单的方法来实现它。
这是我的方法:
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]])
}
}
我用 Scala 编程有一段时间了,但从未依赖过 asInstanceOf
因此我有点害怕将这种代码引入生产环境。你们看到没有演员的方法吗?
这是一个不依赖于 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
, it should have a Traverse
实例,它又提供了一个sequence
方法,也许这就是你想要的。
我想我会用猫来尝试这个并想出了这个:
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,因此 '?'
对于猫来说,这将是一个遍历 either 通过 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)))
我想要一个函数,可以将 Either[A, B]
的任何可迭代类型 C[_]
转换为 Either[C[A], C[B]]
。
我成功了,但我使用了 asInstanceOf
方法,我觉得这种方法在某些情况下可能会失败(我还不知道那会是什么场景,因为我不太明白 CanBuildFrom
解决).
我认为我应该使用自定义实现它 CanBuildFrom
但我希望有更简单的方法来实现它。
这是我的方法:
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]])
}
}
我用 Scala 编程有一段时间了,但从未依赖过 asInstanceOf
因此我有点害怕将这种代码引入生产环境。你们看到没有演员的方法吗?
这是一个不依赖于 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
, it should have a Traverse
实例,它又提供了一个sequence
方法,也许这就是你想要的。
我想我会用猫来尝试这个并想出了这个:
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,因此 '?'
对于猫来说,这将是一个遍历 either 通过 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)))