Cats proporciona una class type adicional llamada MonadError que abstrae los tipos de datos similares a los que se usan para el manejo de errores. MonadError proporciona operaciones adicionales para generar y manejar errores.
package cats
trait MonadError[F[_], E] extends Monad[F] {
// Lift an error into the `F` context:
def raiseError[A](e: E): F[A]
// Handle an error, potentially recovering from it:
def handleErrorWith[A](fa: F[A])(f: E => F[A]): F[A]
// Handle all errors, recovering from them:
def handleError[A](fa: F[A])(f: E => A): F[A]
// Test an instance of `F`,
// failing if the predicate is not satisfied:
def ensure[A](fa: F[A])(e: E)(f: A => Boolean): F[A]
}
MonadError se define en términos de dos parámetros de tipo:
• F es el tipo de la mónada;
• E es el tipo de error contenido en F.
Para demostrar cómo encajan estos parámetros, aquí hay un ejemplo en el que instanciamos la clase de tipo Either:
import cats.MonadError
import cats.instances.either._ // for MonadError
type ErrorOr[A] = Either[String, A]
val monadError = MonadError[ErrorOr, String]
Los dos métodos más importantes de MonadError son raiseError y handleErrorWith. raiseError es como el método puro para Monad excepto que crea una instancia que representa una falla:
val success = monadError.pure(42)
// success: ErrorOr[Int] = Right(42)
val failure = monadError.raiseError("Badness")
// failure: ErrorOr[Nothing] = Left("Badness")
handleErrorWith es el complemento de raiseError. Nos permite consumir un error y (posiblemente) convertirlo en un éxito, similar al método de recuperación de Future:
monadError.handleErrorWith(failure) {
case "Badness" =>
monadError.pure("It's ok")
case _ =>
monadError.raiseError("It's not ok")
}
// res0: ErrorOr[String] = Right("It's ok")
Si sabemos que podemos manejar todos los errores posibles, podemos usar handleWith.
monadError.handleError(failure) {
case "Badness" => 42
case _ => -1
}
// res1: ErrorOr[Int] = Right(42)
Hay otro método útil llamado ensure que implementa un comportamiento similar al de un filtro. Probamos el valor de una mónada exitosa con un predicado y especificamos un error para generar si el predicado devuelve falso:
monadError.ensure(success)("Number too low!")(_ > 1000)
// res2: ErrorOr[Int] = Left("Number too low!")
Cats proporciona instancias de MonadError para numerosos tipos de datos, incluidos Either, Future y Try. La instancia de Either se puede personalizar para cualquier tipo de error, mientras que las instancias de Future y Try, siempre representan errores como Throwables:
import scala.util.Try
import cats.instances.try_._ // for MonadError
val exn: Throwable = new RuntimeException("It's all gone wrong")
exn.raiseError[Try, Int]
// res6: Try[Int] = Failure(java.lang.RuntimeException: It's all gone wrong)