Different monads in for comprehension

434 views Asked by At

I have below code


def processInfoAndReturnResponse(input: Input]): EitherT[Future, CustomException, A] = ???

def sendMessage(message: A): monix.eval.Task[Boolean] = ???

def anotherMethod(message: Input): Future[Either[CustomException, Unit]]= ???
def integrate(): Future[Either[GoogleException, A]] = {
(for {
  response <- processInfoAndModelResponse(input)
  _ <- EitherT.liftF[Future, CustomException, A](sendMessage(response).map(_ => response).runToFuture
}yield response).value

so far all this is good. But now, I want to get boolean from sendMessage and then only if sendMessage returns true, I want to call anotherMethod.

I understand they are different monads. Please let me know a cleaner way how I can add all three calls in for comprehension. Appreciate help

1

There are 1 answers

3
Krzysztof Atłasik On BEST ANSWER

Unfortunately, EitherT and Task are different monads and monads doesn't compose so you can't use them directly in the same for comprehension.

What you could do is lifting Task into EitherT but in this case type parameter F of EitherT would have to be Task and in your case it's Future.

So you have to do 2 things:

  1. Transform Task into Future
  2. Lift Future to EitherT

Let's say your another method looks like this:

def anotherMethod(input: Integer): EitherT[Future, Exception, Unit] = EitherT.rightT[Future, Exception](())

So your for-comprehension could look like this:

import cats.implicits._
import scala.concurrent.ExecutionContext.Implicits._

val prog = for {
    //you need to use leftWiden adjust left of either to common type
    response <- processInfoAndReturnResponse(inp).leftWiden[Exception]
    //running task to Future and then lifting to EitherT
    i <- EitherT.liftF[Future, Exception, Integer](sendMessage(response).runToFuture)
    _ <- anotherMethod(i)
} yield ()

//prog is of type EitherT so we have to unwrap it to regular Future with rethrowT
val future: Future[Unit] = prog.rethrowT

To answer your question after edit, you can use whenA to conditionaly use effect in for-comprehension:

def integrate(): Future[Either[GoogleException, A]] ={
  (for {
    response <- processInfoAndModelResponse(input)
    sendStatus <- EitherT.liftF[Future, CustomException, Boolean](sendMessage(response).runToFuture)
    finalresult <- anotherMethod(input).whenA(sendStatus)
  } yield finalresult).value
}