# Option, Either and Try — Scala

Source: https://www.geekswithgeeks.com/en/scala/p-errors

> Handle missing values and errors as values instead of null and exceptions.

## Errors as values

Idiomatic Scala avoids `null` and uses types to make failure visible. **`Option[A]`** is `Some(value)` or `None` for values that may be absent, such as `map.get(key)` or `list.headOption`. **`Either[E, A]`** is `Left(error)` or `Right(value)`, ideal for validation and domain errors, with the error type documenting what can go wrong; Either is right-biased, so `map` and `flatMap` work on the success side. **`Try[A]`** is `Success(value)` or `Failure(throwable)`, wrapping code that may throw, such as parsing or Java APIs. All three support `map`, `flatMap`, `getOrElse`, `fold` and **for comprehensions**, so a sequence of fallible steps stops at the first failure without nested `if` checks. Convert between them with `toOption`, `toEither` and `toRight(error)`. Reserve **exceptions** for truly unexpected failures, and wrap Java code that may return `null` with `Option(javaValue)`. Libraries such as Cats add `Validated` to **accumulate** all validation errors rather than stopping at the first.

## Validating a signup with Either

Each step can fail; the for comprehension short-circuits on the first Left.

```scala
import scala.util.Try

case class Signup(name: String, email: String, age: Int)

enum SignupError:
  case MissingName
  case InvalidEmail(value: String)
  case InvalidAge(value: String)

def validName(n: String): Either[SignupError, String] =
  if n.trim.nonEmpty then Right(n.trim) else Left(SignupError.MissingName)

def validEmail(e: String): Either[SignupError, String] =
  Either.cond(e.contains("@"), e.toLowerCase, SignupError.InvalidEmail(e))

def validAge(raw: String): Either[SignupError, Int] =
  Try(raw.toInt).toOption.filter(a => a >= 13 && a <= 120).toRight(SignupError.InvalidAge(raw))

def signup(form: Map[String, String]): Either[SignupError, Signup] =
  for
    name  <- validName(form.getOrElse("name", ""))
    email <- validEmail(form.getOrElse("email", ""))
    age   <- validAge(form.getOrElse("age", ""))
  yield Signup(name, email, age)

println(signup(Map("name" -> "Asha", "email" -> "ASHA@example.com", "age" -> "29")))
// Right(Signup(Asha,asha@example.com,29))
println(signup(Map("name" -> "Ravi", "email" -> "ravi", "age" -> "x")))
// Left(InvalidEmail(ravi))

val maybeCity: Option[String] = Option(System.getenv("CITY"))   // wraps a possibly-null Java value
println(maybeCity.map(_.toUpperCase).getOrElse("UNKNOWN"))
```

## Labelled parcels

Option, Either and Try are parcels with a label on the outside: "maybe empty", "success or a reason for failure", "success or an exception". You must look at the label before you can use the contents, so nothing explodes in your hands unexpectedly.

**Quiz:** When is Either[E, A] a better choice than Option[A]?

- [ ] When you do not care why something failed
- [x] When you need to report why a computation failed
- [ ] When values can never be missing
- [ ] When working only with Java code

*Answer:* When you need to report why a computation failed. Either carries an error value on the Left side, while None carries no reason.
