Typed error handling makes failure paths visible in signatures and composable.
Idiomatic Scala avoids null and avoids exceptions for expected failures. Three types make absence and failure explicit:
Option[A]isSome(a)orNone. Use it for values that may be missing:map.get(key),list.find(...),headOption. Wrap Java values that may be null withOption(x).Either[E, A]isLeft(error)orRight(value). It is right-biased:mapandflatMapwork on theRightand pass aLeftthrough unchanged. Use it when the caller needs to know why something failed, with a typed error (often an enum).Try[A]isSuccess(a)orFailure(throwable).Try(expr)catches non-fatal exceptions, which makes it the bridge from exception-throwing (often Java) code.
All three share an API: map, flatMap, filter, getOrElse, fold, foreach and conversions like toOption, toEither and toRight(err). Pattern matching works too.
Don't call .get on an Option or Try: it throws when empty and defeats the purpose. Chain transformations and decide at the edge of the program what to do with the error. Chaining several steps that can fail is cleanest with a for comprehension (next lesson).
Example
import scala.util.{Try, Success, Failure}
val ages = Map("ana" -> 31)
println(ages.get("ana").map(_ + 1)) // Some(32)
println(ages.get("bo").getOrElse(0)) // 0
println(Option(null: String)) // None
enum ParseError:
case Empty
case NotANumber(input: String)
def parse(s: String): Either[ParseError, Int] =
if s.isEmpty then Left(ParseError.Empty)
else s.toIntOption.toRight(ParseError.NotANumber(s))
println(parse("42").map(_ * 2)) // Right(84)
println(parse("x4").map(_ * 2)) // Left(NotANumber(x4))
println(parse("").fold(e => s"error: $e", n => s"ok $n")) // error: Empty
val t = Try("12a".toInt)
t match
case Success(n) => println(n)
case Failure(ex) => println(ex.getClass.getSimpleName) // NumberFormatException
println(Try(10 / 2).toOption) // Some(5)Remember
Optionfor absence,Eitherfor typed errors,Tryto catch exceptions- All support map, flatMap, getOrElse and fold
- Either is right-biased: map works on the success value
- Never call
.get; handle the empty case explicitly
Watch out for
- Wrapping everything in Try, losing the specific error type that Either with an error enum would give.