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"

Leave a Comment

Your email address will not be published. Required fields are marked *