Manejo de Excepciones con Try y Either en Programación Funcional
Resumen
¿Qué es Try y cómo se utiliza en programación funcional?
En el ámbito de la programación, lidiar con excepciones de manera eficiente es crucial para evitar errores inesperados. En la programación funcional, el enfoque hacia el manejo de excepciones es transformado mediante el uso de estructuras como Try. Este enfoque permite tratar las excepciones como valores para manejar el flujo del programa de manera controlada. A menudo, se compara con el uso de Option para manejar valores nulos, pero con un propósito más orientado a las excepciones.
¿Cómo funciona Try?
Try se estructura de manera similar a Option, con la diferencia de que se enfoca en manejar excepciones en lugar de valores nulos. Esta estructura principal, Try, se divide en dos especializaciones:
Failure: Representa una ejecución fallida debido a una excepción.
Success: Indica una ejecución exitosa sin excepciones.
En un contexto de programación operativa, las excepciones son tratadas como eventos disruptivos que deben ser gestionados inmediatamente, mientras que en programación funcional, las excepciones deben tratarse como valores que permitan al programador razonar claramente sobre el flujo del programa.
¿Cómo importar y utilizar Try?
Para utilizar Try en Scala, es necesario importarlo explícitamente, ya que no está disponible por defecto, aunque sí forma parte de las bibliotecas base del lenguaje. El proceso sería el siguiente:
importscala.util.Try
Una vez importado, Try puede ser utilizado de manera similar a Option, aplicando un map para manejar casos exitosos (Success) y getOrElse para gestionar los fallidos (Failure).
Ejemplo básico de Try
Supongamos que queremos manejar una excepción utilizada a raíz de un valor nulo:
val a = Try(null.head)a match{case scala.util.Success(value)=> println(s"Success: $value")case scala.util.Failure(exception)=> println(s"Failure: $exception")}
En este caso, al intentar acceder a head de un null, se producirá una excepción, pero no explotará el sistema sino que almacenará la excepción dentro de a como un tipo Failure.
¿Qué es Either y cómo se emplea?
Either es otra forma de gestionar situaciones de éxito o fracaso que se puede emplear de una manera aún más general que Try. Esta estructura proporciona dos lados:
Right: Se usa para lo que se considera el "camino feliz" o el flujo esperado.
Left: Se utiliza cuando algo no sale como se planeó, desviándose del camino óptimo.
¿Cuál es la diferencia con Try?
A diferencia de Try, Either no se importa explícitamente, ya que está presente directamente al igual que Option. Either es útil en situaciones donde se desee definir claramente cuáles son las posibles salidas de un proceso (diferente de las excepciones claras que maneja Try).
Ejemplo básico de Either
Usando Either, puedes definir lados con tipos específicos y operar sobre el "camino feliz":
val result: Either[String,Int]= Right(10).map(_ +1)// Resulta en Right(11)val errorResult: Either[Int,Int]= Left(10).map(_ +1)// Resulta en Left(10)
Aquí se observa que map opera sobre el lado derecho (Right) creciendo el valor en uno. En el caso del lado izquierdo (Left), la operación map no afecta al valor.
Consejos prácticos para la implementación
Planificación del flujo: Usa Try y Either para prever posibles fallos y bifurcaciones en la lógica del programa, permitiendo un diseño más limpio y mantenible.
Manejo explícito de excepciones: Evita las explosiones no controladas usando estas estructuras para manejar errores de manera más transparente.
Refuerzo de tipología: Con Either, especifica los tipos que pueden ocupar los lados para guiar al compilador y evitar errores de tipología.
Adoptar Try y Either en tu diseño no solo refuerza la robustez de tu código, sino también te acerca a prácticas más avanzadas y generalizadas de la programación funcional. Continúa explorando y aplicando estos conceptos para mejorar la calidad de tu software de manera significativa.
No me funciona el ejemplo de con el Right ni con el Left, me sale lo siguiente:
scala>Right(10).map(_ +1)<console>:12: error: value map is not a member of scala.util.Right[Nothing,Int]Right(10).map(_ +1)^
¿Que solución hay para esye caso ?
Es curioso. A mí me funciona bien, pero cuando uso Right(10) mi respuesta es tipo scala.util.Right. Cuando le aplido el map es tipo scala.util.Either. Aunque creo que debe ser más por la versión de Scala que estés usando. Yo uso Scala 2.13.7 y en la documentación map está disponible en ambas.
Es raro tu caso, porque a mí también me funciona bien.
Disyunciones: Either, try
Try
Similar a Opion, pero en el contexto de las excepciones ¿Son las excepciones en verdad tan terribles? Pista: Sí
La idea es tratar las excepciones cono valores, y no saltar abruptamente en la ejecución del código. De esta manera no hay que chequear por excepciones en cada sección de código
Either
Similar a Option y Try, pero aplicable a cualquer contexto. Una vez entendemos esta manera de componer nuestros programas, nos aseguramos de cubrir casi todas las posibilidades
importscala.utils.Try
val a = Try(Nil.head)//scala.util.Try[Nothing] = Failure(java.util.NoSucElementException: head of empty list)a.map(x =>s"$x")Right(10).map(_ +1)//res: scala.util.Either[Nothing,Int] = Right(11)Left(10).map(_ +1)// errorLeftInt,Int.map(_ +1)// error//res: scala.util.Either[Nothing,Int] = Left(10)
Uff con el Try controlamos mucho las exceptiones, lo cual es de mucha ayuda, cuando estamos controlando esa parte
Para tener el Try disponible en nuestro codigo, usamos el import, asi:
import scala.util.Try
Either, cuando queramos que nuestro codigo, valla por la parte correcta, se ira por el lado derecho y cuando va por el camino no esperado va por el izquierdo
Si uso Right, también puedo especificarle los tipos que va a recibir?