I'm using cats, wonder how to turn a data with it:
val data = NonEmptyList[Either[Error, User]]
to
val target: Either[Error, NonEmptyList[User]] = howToConvert(data)
Usually when you want to turn the type constructors inside out you are probably looking for sequence
.
It will do the expected thing of either returning the first error, or all users if there is no error. If we pretend users are ints and errors are strings:
scala> import cats.data.NonEmptyList, cats.syntax.all._
import cats.data.NonEmptyList
import cats.syntax.all._
scala> val data: NonEmptyList[Either[Error, User]] = NonEmptyList.of(Right(2), Left("error1"), Right(4))
val data: cats.data.NonEmptyList[Either[Error,User]] = NonEmptyList(Right(2), Left(error1), Right(4))
scala> data.sequence
val res4: Either[Error,cats.data.NonEmptyList[User]] = Left(error1)
Often when you need to use sequence
that's a sign that you should have used traverse
instead of map
a little bit earlier in your code, i.e.
scala> val data: NonEmptyList[User] = NonEmptyList.of(2, 3, 4)
val data: cats.data.NonEmptyList[User] = NonEmptyList(2, 3, 4)
scala> data.traverse(user => if (user % 2 == 0) Right(user) else Left("error1"))
val res3: Either[String,cats.data.NonEmptyList[User]] = Left(error1)
The below explanation is only interesting if for some reason you are using a scala version older than 2.13.
If you have -Ypartial-unification
turned on in Scala >= 2.11.9 you can just let the compiler infer everything:
data.sequence
Otherwise:
type EitherError[A] = Either[Error, A]
data.sequence[EitherError, User]
Or if you have the type lambda plugin:
data.sequence[Either[Error, ?], User]
Or if you don't have the plugin, but you dislike type aliases:
data.sequence[({type L[A] = Either[Error, A]})#L, User]