Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic sequence of Either to Either of sequence with preserved type

I would like to have a function that converts any iterable type C[_] of Either[A, B] to Either[C[A], C[B]].

I got it working but I used asInstanceOf method and I feel like this approach can fail in some scenario (I don't yet know what scenerio that would be since I don't quite understand CanBuildFrom resolving).

I think that I should implement it using custom CanBuildFrom but I hope there is easier way of doing that.

Here's my approach:

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]])
  }
}

I've been programming in Scala for a while but have never relied on asInstanceOf and thus I am a bit afraid of introducing this kind of code to a production environment. Do you guys see a way of doing this without a cast?

like image 630
bottaio Avatar asked Jul 22 '18 18:07

bottaio


1 Answers

With cats it would be a function that traverses eithers through Validated and back to Either. The reason to convert to Validated is that sequence requires Applicative instance.

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)))
like image 189
Nazarii Bardiuk Avatar answered Sep 21 '22 06:09

Nazarii Bardiuk