Translate

martes, 28 de junio de 2022

Mónadas en Cats parte 13

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)