I have the following function:
def function(i: Int): IO[Either[String, Option[Int]]] = ???
I want a function of the form:
def foo(either: Either[String, Option[Int]]): IO[Either[String, Option[Int]]]
and I want it to have the following behavior:
def foo1(either: Either[String, Option[Int]])
: IO[Either[String, Option[Int]]] = either match {
case Right(Some(i)) => bar(i)
case Right(None) => IO.pure(None.asRight)
case Left(s) => IO.pure(s.asLeft)
}
I want to do it less explicitly, so I tried EitherT:
def foo2(either: Either[String, Option[Int]]):
IO[Either[String, Option[Int]]] = {
val eitherT = for {
maybe <- EitherT.fromEither[IO](either)
int <- EitherT.fromOption(maybe, "???")
x <- EitherT(bar(int))
} yield x
eitherT.value
}
but this means that Right(None)
will be mapped to IO(Left("???"))
which is not what I want.
is there an alternative formulation with EitherT
without a match expression that is equivalent to the foo1
implementation?
more importantly, how would an implementation that uses map/traverse/biTraverse/etc.
(and doesn't match on any of option/eithers) look like?
p.s. The intention here is to define a "map" function for the following type:
trait Lookup[F[_], K, A] {
def get(key: K): F[Either[FormatError, Option[A]]]
}
without match
import cats._
import cats.data._
import cats.implicits._
def bar[F[_] : Applicative](i: Int): F[Either[String, Option[Int]]] =
(i + 1).some.asRight[String].pure[F]
def foo[F[_] : Applicative : Monad : FlatMap](either: Either[String, Option[Int]]): F[Either[String, Option[Int]]] =
OptionT(EitherT(either.pure[F])).flatMap { i =>
OptionT(EitherT(bar[F](i)))
}.value.value
foo[Id](1.some.asRight)
//res0: cats.Id[Either[String,Option[Int]]] = Right(Some(2))