← Scala EspañolChapter 11 of 13

Manejo de Errores

## Objetivos de Aprendizaje - Dominar el tipo Option para seguridad frente a nulos - Trabajar con Try para manejo de excepciones - Usar Either para mensajes de error - Comprender for comprehensions con tipos de error - Aprender mejores practicas para manejo de errores ## Option Option representa un valor que puede existir o no: ```scala val someValue: Option[Int] = Some(5) val noValue: Option[Int] = None ``` ### Creando Options ```scala // De valor nullable val name: String = null val optionName: Option[String] = Option(name) // None val name2: String = "Alice" val optionName2: Option[String] = Option(name2) // Some("Alice") // De Map val map = Map("a" -> 1, "b" -> 2) map.get("a") // Some(1) map.get("c") // None ``` ### Extrayendo Valores ```scala val option: Option[Int] = Some(42) // Emparejamiento de patrones option match { case Some(value) => println(s"Got: $value") case None => println("No value") } // getOrElse val result = option.getOrElse(0) // 42 val noResult = None.getOrElse(0) // 0 // orElse val alt = None.orElse(Some(10)) // Some(10) // isDefined, isEmpty option.isDefined // true option.isEmpty // false ``` ### Map en Option ```scala val option: Option[Int] = Some(5) option.map(_ * 2) // Some(10) option.map(_ + 10) // Some(15) None.map(_ * 2) // None // FlatMap en Option def parseInt(s: String): Option[Int] = { try Some(s.toInt) catch case _: Exception => None } Some("42").flatMap(parseInt) // Some(42) Some("abc").flatMap(parseInt) // None None.flatMap(parseInt) // None ``` ### Filtrado ```scala val option: Option[Int] = Some(5) option.filter(_ > 10) // None (5 no es > 10) option.filter(_ > 3) // Some(5) // withFilter (no envuelve en Option) option.withFilter(_ > 10).foreach(println) // Sin salida ``` ## Try Try representa un calculo que puede lanzar una excepcion: ```scala import scala.util.{Success, Failure, Try} val success: Try[Int] = Success(42) val failure: Try[Int] = Failure(new Exception("Oops")) ``` ### Creando Try ```scala import scala.util.Try // De codigo que puede lanzar def parseInt(s: String): Try[Int] = Try(s.toInt) parseInt("42") // Success(42) parseInt("abc") // Failure(NumberFormatException) // Usando Try con Option def safeDivision(a: Int, b: Int): Try[Int] = Try(a / b) safeDivision(10, 2) // Success(5) safeDivision(10, 0) // Failure(ArithmeticException) ``` ### Extrayendo Valores de Try ```scala val tryValue: Try[Int] = Try(42 / 2) // Emparejamiento de patrones tryValue match { case Success(v) => println(s"Got: $v") case Failure(ex) => println(s"Error: ${ex.getMessage}") } // getOrElse tryValue.getOrElse(0) // 21 // orElse def failing: Try[Int] = Try(throw new Exception("Oops")) failing.orElse(Success(0)) // Success(0) ``` ### Map y FlatMap en Try ```scala val tryValue: Try[Int] = Success(5) tryValue.map(_ * 2) // Success(10) tryValue.map(_ + 10) // Success(15) Failure(new Exception("Oops")).map(_ * 2) // Failure // FlatMap def safeSquare(x: Int): Try[Int] = Try { if (x > 10) throw new Exception("Too large") else x * x } Success(5).flatMap(safeSquare) // Success(25) Success(15).flatMap(safeSquare) // Failure ``` ### Recover ```scala def parseInt(s: String): Try[Int] = Try(s.toInt) parseInt("abc").recover { case _: NumberFormatException => 0 } // Success(0) parseInt("abc").recoverWith { case _: NumberFormatException => Try(42) } // Success(42) // Recover con funcion parcial parseInt("abc").recoverWith { case _: NumberFormatException => Success(0) case _: Exception => Failure(...) } // Success(0) ``` ## Either Either representa uno de dos tipos: ```scala val left: Either[String, Int] = Left("Error") val right: Either[String, Int] = Right(42) ``` ### Creando Either ```scala def parseInt(s: String): Either[String, Int] = { try Right(s.toInt) catch case e: Exception => Left(e.getMessage) } parseInt("42") // Right(42) parseInt("abc") // Left("For input string: abc") ``` ### Extrayendo Valores de Either ```scala val either: Either[String, Int] = Right(42) // Emparejamiento de patrones either match { case Right(v) => println(s"Got: $v") case Left(msg) => println(s"Error: $msg") } // getOrElse (solo en Right) either.right.getOrElse(0) // 42 Left("error").right.getOrElse(0) // 0 // Swap either.swap // Left(42) - no util aqui ``` ### Map en Either ```scala val right: Either[String, Int] = Right(5) right.map(_ * 2) // Right(10) right.map(_ + 10) // Right(15) Left("error").map(_ * 2) // Left("error") - preservado // FlatMap def safeSquare(x: Int): Either[String, Int] = { if (x > 10) Left("Too large") else Right(x * x) } Right(5).flatMap(safeSquare) // Right(25) Right(15).flatMap(safeSquare) // Left("Too large") ``` ### For Comprehensions con Either ```scala def parseInt(s: String): Either[String, Int] = try Right(s.toInt) catch case e: Exception => Left(e.getMessage) for { a <- parseInt("10") b <- parseInt("20") c <- parseInt("30") } yield a + b + c // Right(60) for { a <- parseInt("10") b <- parseInt("abc") // Esto falla c <- parseInt("30") } yield a + b + c // Left("For input string: abc") ``` ## For Comprehensions con Option ```scala case class User(name: String, email: Option[String]) def getUser(id: Int): Option[User] = ??? def getEmailDomain(email: String): Option[String] = ??? val domain = for { user <- getUser(1) email <- user.email domain <- getEmailDomain(email) } yield domain // Sin for comprehension val domainAlt = getUser(1).flatMap { user => user.email.flatMap { email => getEmailDomain(email) } } ``` ## Encadenando Operaciones ### Usando fold ```scala def parseInt(s: String): Option[Int] = Try(s.toInt).toOption List("1", "2", "abc", "4") .map(parseInt) .fold(0)(_ + _) // 0 + 1 + 2 + 0 = 3 (abc se convierte en 0) // O usando collect List("1", "2", "abc", "4") .map(parseInt) .collect { case Some(n) => n } .sum // 7 ``` ### Usando sequence ```scala import scala.util.TryingAnd Sequencing._ val options: List[Option[Int]] = List(Some(1), Some(2), Some(3)) val noneOptions: List[Option[Int]] = List(Some(1), None, Some(3)) options.sequence // Some(List(1, 2, 3)) noneOptions.sequence // None // traverse def parseInt(s: String): Option[Int] = Try(s.toInt).toOption List("1", "2", "3").traverse(parseInt) // Some(List(1, 2, 3)) List("1", "abc", "3").traverse(parseInt) // None ``` ## Mejores Practicas 1. **Preferir Option sobre null** 2. **Usar Try para codigo que lanza excepciones** 3. **Usar Either para fallas esperadas con mensajes** 4. **Encadenar operaciones en lugar de if-else anidados** 5. **Usar for comprehensions para manejo de errores limpio** 6. **No capturar Exception innecesariamente** 7. **Devolver mensajes de error significativos en Either** ## Resumen - Option maneja valores opcionales (Some o None) - Try maneja excepciones (Success o Failure) - Either maneja fallas esperadas (Left para error, Right para exito) - map, flatMap, filter funcionan en estos tipos - Las for comprehensions proporcionan sintaxis limpia para encadenar - Usar getOrElse para proporcionar valores por defecto - recover y recoverWith manejan fallas - sequence y traverse trabajan con colecciones de tipos de error

Comments

Comments powered by Giscus

To enable comments, add your Giscus embed code here.

Learn more about Giscus →