简体   繁体   中英

Cats get value from the monad stack

I have a monad stack for responses from my components implemented with the cats monads transformers:

type FutureEither[A] = EitherT[Future, Error, A] 
type FutureEitherOption[A] = OptionT[FutureEither, A]

the result is effectively:

Future[Either[Error, Option[A]]]

How can I get a value or error from this stack in a proper way? How can I combine the results from several calls execute in parallel in a proper way? For example in such scenario:

def fooServiceCall: FutureEitherOption[Foo]
def barServiceCall(f: Option[Foo]): FutureEitherOption[Bar]

for {
  r1 <- fooServiceCall
  r2 <- barServiceCall(r1)
} yield r2

Your second method barServiceCall tells in its signature that it can deal with Option[Foo] directly, instead of relying on the monad transformer stack to fail with None at some point. Therefore, you have to unpack one layer of OptionT[EitherT[Future, Error, ?], A] , and instead deal with EitherT[Future, Error, Option[A]] directly: even though your methods seem to return results in the former monad stack, the right tool in this for-comprehension is the latter one.

Recall that if o: OptionT[F, A] , then the wrapped o.value is of type F[Option[A]] .

Therefore, if you simply call .value on a OptionT[EitherT[Future, Error, ?], A] , you get the required EitherT[Future, Error, Option[A]] . Here is how it works out in code:

import scala.concurrent.Future
import scala.util.Either
import cats.instances.future._
import cats.instances.either._
import cats.instances.option._
import cats.data.EitherT
import cats.data.OptionT
import scala.concurrent.Await
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._

type Error = String        // or whatever...
type Foo = (Int, Int)      // whatever...
type Bar = (String, Float) // whatever... 

type FutureEither[A] = EitherT[Future, Error, A]
type FutureEitherOption[A] = OptionT[FutureEither, A]

def fooServiceCall: FutureEitherOption[Foo] = ???
def barServiceCall(f: Option[Foo]): FutureEitherOption[Bar] = ???


val resFut: Future[Either[Error, Option[Bar]]] = (for {
  r1 <- fooServiceCall.value // : EitherT[Future, Error, Option[Foo]]
  r2 <- barServiceCall(r1).value // : EitherT[Future, Error, Option[Bar]]
} yield r2).value

val res: Either[Error, Option[Bar]] = Await.result(resFut, 10.seconds)

Now the result is simple enough that you can deal with it direcly.

Alternatively, if you don't want to unpack the result here and now, you can wrap it again into an OptionT and keep working with a FutureEitherOption[Bar] :

val res2: FutureEitherOption[Bar] = 
  OptionT(for {
    r1 <- fooServiceCall.value
    r2 <- barServiceCall(r1).value
  } yield r2)

You can call .value on a OptionT[F, A] to get a F[Option[A]] . And just do OptionT() to do the reverse. I believe EitherT has similar methods that you can use to wrap and unwrap these classes when you need access to internal Option s or Either s. For example, I think you could do something like:

val x: FutureEither[Option[Bar]] = for {
  r1 <- fooServiceCall.value
  r2 <- barServiceCall(r1).value
} yield r2
val result: FutureEitherOption[Bar] = OptionT(x)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM