Scala Either Type
Either[L, R] represents a value that is one of two possibilities: a Left (typically an error or failure) or a Right (typically a success). While Option says "present or absent," Either says "this result or that error" — and it carries information about the failure, not just that one occurred.
Either Structure
Either[L, R]
│
├── Left(value: L) → failure case (error, message, exception)
│
└── Right(value: R) → success case (the result you wanted)
Convention: Left = error / bad / wrong
Right = success / good / correct ("right" = correct)
Basic Either Usage
def divide(a: Int, b: Int): Either[String, Double] =
if b == 0 then Left("Cannot divide by zero")
else Right(a.toDouble / b)
println(divide(10, 2)) // Right(5.0)
println(divide(5, 0)) // Left(Cannot divide by zero)
Pattern Matching on Either
def parseAge(input: String): Either[String, Int] =
try
val age = input.toInt
if age >= 0 && age <= 120 then Right(age)
else Left(s"Age $age is out of range (0–120)")
catch
case _: NumberFormatException => Left(s"'$input' is not a number")
val inputs = List("25", "abc", "-3", "200", "42")
inputs.foreach { input =>
parseAge(input) match
case Right(age) => println(s"Valid age: $age")
case Left(error) => println(s"Error: $error")
}
// Valid age: 25
// Error: 'abc' is not a number
// Error: Age -3 is out of range (0–120)
// Error: Age 200 is out of range (0–120)
// Valid age: 42
map and flatMap on Either
map and flatMap on Either only apply to the Right side. If the value is Left, it passes through unchanged:
val result1: Either[String, Int] = Right(42)
val result2: Either[String, Int] = Left("missing")
println(result1.map(_ * 2)) // Right(84)
println(result2.map(_ * 2)) // Left(missing) — unchanged
println(result1.map(_.toString)) // Right(42)
Chaining with flatMap
def findUser(id: Int): Either[String, String] =
if id > 0 then Right(s"User_$id") else Left("Invalid user ID")
def fetchScore(user: String): Either[String, Int] =
if user.startsWith("User_") then Right(95) else Left("User not found")
def grade(score: Int): Either[String, String] =
if score >= 90 then Right("A")
else if score >= 75 then Right("B")
else Left("Below pass threshold")
val pipeline = findUser(1)
.flatMap(fetchScore)
.flatMap(grade)
println(pipeline) // Right(A)
val failPipeline = findUser(-1)
.flatMap(fetchScore)
.flatMap(grade)
println(failPipeline) // Left(Invalid user ID) — short-circuits
findUser(1) → Right("User_1")
fetchScore(...) → Right(95)
grade(95) → Right("A") ← final result
findUser(-1) → Left("Invalid user ID")
fetchScore(...) → Left("Invalid user ID") ← skipped
grade(...) → Left("Invalid user ID") ← skipped
For Comprehension with Either
case class Registration(name: String, email: String, age: Int)
def validateName(name: String): Either[String, String] =
if name.trim.length >= 2 then Right(name.trim)
else Left("Name must be at least 2 characters")
def validateEmail(email: String): Either[String, String] =
if email.contains("@") then Right(email)
else Left("Invalid email format")
def validateAge(age: Int): Either[String, Int] =
if age >= 18 then Right(age)
else Left("Must be 18 or older")
def register(name: String, email: String, age: Int): Either[String, Registration] =
for
n <- validateName(name)
e <- validateEmail(email)
a <- validateAge(age)
yield Registration(n, e, a)
println(register("Priya", "priya@example.com", 25))
// Right(Registration(Priya,priya@example.com,25))
println(register("P", "priya@example.com", 25))
// Left(Name must be at least 2 characters)
println(register("Priya", "invalid-email", 25))
// Left(Invalid email format)
println(register("Priya", "priya@example.com", 16))
// Left(Must be 18 or older)
Converting Either to Option
val success: Either[String, Int] = Right(42)
val failure: Either[String, Int] = Left("Error")
success.toOption // Some(42)
failure.toOption // None
// Swap Left and Right
success.swap // Left(42)
failure.swap // Right("Error")
Either vs Option vs Try
Type Left / None / Failure Right / Some / Success Use when
────────── ─────────────────────── ─────────────────────── ─────────────────────────
Option[A] None (no info) Some(a) Value may be absent
Either[E, A] Left(error: E) Right(a) Operation may fail with reason
Try[A] Failure(exception) Success(a) Exception-throwing code
getOrElse and fold
val result: Either[String, Int] = Right(42)
val error: Either[String, Int] = Left("oops")
result.getOrElse(0) // 42
error.getOrElse(0) // 0
// fold handles both cases
result.fold(err => s"Failed: $err", value => s"Got: $value")
// "Got: 42"
error.fold(err => s"Failed: $err", value => s"Got: $value")
// "Failed: oops"
