Search code examples
scalascala-cats

EitherT + State type mismatch


I'm working on a small program using Scala Cats. I'm encountering lots of type errors when trying to use EitherT with State and for comprehensions. For instance:

import cats.data.{EitherT, State}
import cats.data.State.get

object Test {
  type IntState[T] = State[Int, T]
  type IntStateEither[T] = EitherT[IntState, String, T]

  val test: IntStateEither[Unit] = for {
    isValid <- EitherT.right(get.map((it: Int) => it % 2 == 0))
    _ <- if (!isValid) EitherT.leftT("invalid number") else EitherT.rightT(())  // *
  } yield ()
}

Which gives me:

(...) Test.scala:12: type mismatch;
 found   : cats.data.EitherT[[A(in value <local Id>)]A(in value <local Id>),_1,Unit] where type _1 <: String
 required: cats.data.EitherT[[A(in class IndexedStateT)]cats.data.IndexedStateT[cats.Eval,Int,Int,A(in class IndexedStateT)],String,Unit]
one error found

If I comment out the line marked (*) above, this code compiles. I think I'm following the instructions on the Cats website under "From A or B to EitherT[F, A, B]" correctly, but do I need to give more type hints? If yes, I'm not sure where I can add them.

Any pointers are greatly appreciated!

I'm using Cats 2.0.0 and Scala 2.13.2.


Solution

  • It seems that the problem is that the compiler is not able to infer the correct types for EitherT.leftT and EitherT.rightT.
    You can fix those errors using explicit types like: EitherT.rightT[IntState, String], or if you only need one flatMap call, it seems doing it explicitly does work:

    val test: IntStateEither[Unit] =
      EitherT
        .right[String](State.get[Int].map(it => it % 2 == 0))
        .flatMap { isValid =>
          if (isValid) EitherT.rightT(())
          else EitherT.leftT("invalid number")
        }
    

    PS: It may be worth checking if bm4 helps.