Search code examples
scalacompiler-errorsmonadsscala-cats

Cats MonadError with Try doesn't complie


I just found a MonadError code snippet that uses Either like the following

  import cats.MonadError
  import cats.instances.either._ // for MonadError

  type ErrorOr[A] = Either[String, A]
  val monadError = MonadError[ErrorOr, String]

  val success = monadError.pure(42)
  println(success)
  // success: ErrorOr[Int] = Right(42)

  val failure = monadError.raiseError("Badness")
  println(failure)
  // failure: ErrorOr[Nothing] = Left("Badness")

The above code compiles and gives expected result. Then I tried to do the same for Try

  import cats.MonadError
  import cats.instances.try_._
  type TryOr[A] = Try[A]
  val monadError = MonadError[TryOr, String]

  val success = monadError.pure(42)
  println(success)

  val failure = monadError.raiseError("Badness")
  println(failure)

this code doesn't compile. it says no implicit found for parameter F:MonadError[TryOr, String] I went to the definition of MonadError and found following.

object MonadError {
  def apply[F[_], E](implicit F: MonadError[F, E]): MonadError[F, E] = F
}

But didn't understand why it worked for Either but not working for Try. Why we need additional implicit even after import cats.instances.try_._

Can you help understand what is happening in the background ?


Solution

  • With Try you should use only Throwable as an error type. This is a limitation of the Try design. MonadError[Throwable, A]. You can check Try instances cats.instances.TryInstances. There is only MonadThrow instance.

      type TryOr[A] = Try[A]
      val monadError = MonadThrow[TryOr]
    
      val success = monadError.pure(42)
      println(success)
    
      val failure = monadError.raiseError(new Exception("Badness"))
      println(failure)