Search code examples
scalafunctional-programmingscala-catscats-effect

Scala singleton objects and implicit resolution


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) {

Solution

  • 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])