lunes, 20 de junio de 2022

Mónadas en Cats parte 12

Either se usa típicamente para implementar el manejo de errores rápidos. Secuenciamos los cálculos utilizando flatMap como de costumbre. Si un cálculo falla, los cálculos restantes no se ejecutan:

for {

a <- 1.asRight[String]

b <- 0.asRight[String]

c <- if(b == 0) "DIV0".asLeft[Int]

else (a / b).asRight[String]

} yield c * 100

// res21: Either[String, Int] = Left("DIV0")

Cuando usamos Either para el manejo de errores, necesitamos determinar qué tipo queremos usar para representar los errores. Podríamos usar Throwable para esto:


type Result[A] = Either[Throwable, A]


Esto nos da una semántica similar a scala.util.Try. El problema, sin embargo, es que Throwable es un tipo extremadamente amplio. No tenemos (casi) idea de qué tipo de error ocurrió.

Otro enfoque es definir un tipo de datos algebraicos para representar los errores que pueden ocurrir en nuestro programa:

object wrapper {

   sealed trait LoginError extends Product with Serializable

   final case class UserNotFound(username: String) extends LoginError

   final case class PasswordIncorrect(username: String) extends LoginError

   case object UnexpectedError extends LoginError

}; 

import wrapper._


case class User(username: String, password: String)

type LoginResult = Either[LoginError, User]


Este enfoque resuelve los problemas que vimos con Throwable. Nos da un conjunto fijo de tipos de errores esperados. También obtenemos la seguridad de la comprobación exhaustiva de cualquier coincidencia de patrones que hagamos:

// Choose error-handling behaviour based on type:

def handleError(error: LoginError): Unit =

    error match {

        case UserNotFound(u) => println(s"User not found: $u")

        case PasswordIncorrect(u) => println(s"Password incorrect: $u")

        case UnexpectedError => println(s"Unexpected error")

    }

val result1: LoginResult = User("dave", "passw0rd").asRight

// result1: LoginResult = Right(User("dave", "passw0rd"))

val result2: LoginResult = UserNotFound("dave").asLeft

// result2: LoginResult = Left(UserNotFound("dave"))

result1.fold(handleError, println)

// User(dave,passw0rd)

result2.fold(handleError, println)

// User not found: dave


No hay comentarios.:

Publicar un comentario