Search code examples
scalascala-cats

Nesting a for comprehension inside a yield


I am getting a compile error with this code. I have stripped it down to simplify it as much as possible. I am using cats EitherT monad transformer here.


  def foo(
    id : String
  ): EitherT[Future, Err, Either[A, B]] =
    for {
      a <- f1(id)
    } yield {
      if (a > 2)
        Left(A("Got an a"))
      else {
        for {
          b <- f2() // THIS IS THE PROBLEM
        } yield (Right(B("Got a B"))
      }
    }

The compiler is complaining about f2(). It has a return type of EitherT[Future,Err,C]. The complier error I am getting is:

[error]  found   : cats.data.EitherT[Future,Err,scala.util.Right[Nothing,B]]
[error]  required: Either[A,B]
[error]           b <- f2()

I am not sure why it is complaining about this. I want to do the "if" check as a guard on the call to f2() but I cannot as EitherT does not have a withFilter method. Is there a better way to do this? Basic logic is, make call to f1(). If the value of a > 2 do not call f2 but if it is not then call f2.


Solution

  • Try single flatMap instead of nested for-comprehensions like so

    case class A(v: String)
    case class B(v: String)
    case class C(v: String)
    
    def f1(id: String): EitherT[Future, Err, Long] = EitherT.rightT(42)
    def f2(): EitherT[Future, Err, C] = EitherT.rightT(C("cc"))
    
    def foo(id : String): EitherT[Future, Err, Either[A, B]] =
      f1(id).flatMap { a =>
        if (a > 2)
          EitherT.rightT(Left(A("Got an A")))
        else
          f2().map(b => Right(B("Got a B")))
      }
    
    foo("123").value.andThen(v => println(v))
    

    which outputs

    Success(Right(Left(A(Got an A))))
    

    however I second Krzysztof's suggestion that EitherT[Future, Err, Either[A, B]] models seems a bit unusual.