What is the best way to make implicit resolution in scala work with singleton objects? This is especially common with Either
and custom error objects.
In the code example below a method returns an application-specific error wrapped in IO. The error is represented by a singleton object extending Throwable
. This code does not compile because scala is looking for an implicit for AppSpecificError.type
instead of Throwable
.
It is possible to put everything into variables with a specified type but it looks weird. This seems like a pretty common case, what is the best way to address it?
import cats.data.EitherT
import cats.effect.IO
import cats.implicits._
import scala.util.Random
object EitherTest {
case object AppSpecificError extends Throwable
def random: IO[Boolean] = {
IO(Random.nextBoolean())
}
def appMethod(flag: Boolean): EitherT[IO, Throwable, Int] = {
for {
flag <- EitherT.right(random)
result <- if (flag) {
EitherT.left[Int](AppSpecificError.pure[IO]) // compilation error here
} else {
EitherT.right[Throwable](10.pure[IO])
}
// can be more computations here
} yield result
}
def main(args: Array[String]): Unit = {
appMethod(true).value.unsafeRunSync() match {
case Right(s) => println("Success")
case Left(error) => println(error)
}
}
}
Error:(18, 14) type mismatch;
found : cats.data.EitherT[cats.effect.IO,_1,Int] where type _1 >: EitherTest.AppSpecificError.type <: Throwable
required: cats.data.EitherT[cats.effect.IO,Throwable,Int]
Note: _1 <: Throwable, but class EitherT is invariant in type A.
You may wish to define A as +A instead. (SLS 4.5)
result <- if (flag) {
Try to specify type parameters explicitly
EitherT.left[Int][IO, Throwable](AppSpecificError.pure[IO])
or use type ascription
EitherT.left[Int]((AppSpecificError: Throwable).pure[IO])