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 →