Kotlin Academy · Lesson

Either<L, R>: Typed Error Handling Without Exceptions

Use Either to represent success and failure without throwing exceptions.

Lesson 1 of 413 steps

Either<L, R>: Typed Error Handling Without Exceptions is a free Kotlin Academy lesson on CoddyKit. This is lesson 1 of 4. Any 3 lessons of this learning path are free to read in full — after that, CoddyKit PRO unlocks every lesson, plus hands-on practice with a built-in code editor and a 24/7 AI tutor. It is part of the Kotlin Academy learning path, and your progress syncs across the web and the CoddyKit app. The Kotlin Academy course includes 4 lessons in total.

The Problem with Exceptions for Business Logic

Exceptions are designed for unexpected failures (null pointer, IO error). Using them to signal expected business failures (validation failure, "not found") makes control flow invisible, forces try/catch at every call site, and hides errors in function signatures.

What Is Either<L, R>?

Either is a sum type with two cases: Left(value: L) conventionally holds the error, and Right(value: R) conventionally holds the success value. A function returning Either makes the possibility of failure explicit in its signature.

Arrow's Either

Add Arrow to your project to get Either and its full ecosystem of operators:

// build.gradle.kts
implementation("io.arrow-kt:arrow-core:1.2.4")

// Usage
import arrow.core.Either
import arrow.core.left
import arrow.core.right

Returning Either from a Function

Return value.right() for success and error.left() for failure. The caller must handle both branches:

sealed class UserError { object NotFound : UserError(); data class InvalidEmail(val msg: String) : UserError() }

fun findUser(id: Long): Either<UserError, User> =
    if (id <= 0) UserError.NotFound.left()
    else User(id, "Alice").right()

Consuming Either with fold

Use fold(ifLeft, ifRight) to handle both cases in one expression:

val result = findUser(1L)
val message = result.fold(
    ifLeft  = { error -> "Error: $error" },
    ifRight = { user  -> "Found: ${user.name}" }
)
println(message)

Transforming the Right Value with map

map { } transforms the Right value without touching a Left. This makes Either a functor — you can chain transformations safely:

val nameResult: Either<UserError, String> = findUser(1L).map { it.name }

Chaining with flatMap

flatMap { } chains computations that themselves return Either. If any step returns Left, the chain short-circuits and the error propagates without executing subsequent steps:

fun validateEmail(email: String): Either<UserError, String> =
    if (email.contains("@")) email.right()
    else UserError.InvalidEmail("bad format").left()

fun createUser(email: String): Either<UserError, User> =
    validateEmail(email).flatMap { validEmail ->
        User(1L, validEmail).right()
    }

Either.catch for Exception Wrapping

Either.catch { } runs a block and wraps any exception as a Left. Use it at the boundary between legacy code that throws and your Either-based domain:

val result: Either<Throwable, User> = Either.catch {
    userRepository.findOrThrow(id)
}

Recovering with getOrElse and orElse

getOrElse { default } extracts the Right value or returns a default. orElse { alternativeEither } replaces a Left with another computation:

val user: User = findUser(0L).getOrElse { User(-1L, "Guest") }
val user2: Either<UserError, User> = findUser(0L).orElse { User(99L, "Default").right() }

Pattern Matching with when

Use when on a sealed Either-like class or on Either itself with is Either.Left / Either.Right to exhaustively handle cases:

when (val r = findUser(1L)) {
    is Either.Left  -> println("Error: ${r.value}")
    is Either.Right -> println("User: ${r.value.name}")
}

Either in Service Layers

Return Either from repository and service functions. Map it to HTTP responses at the Ktor route level, keeping business logic free from framework concerns:

get("/users/{id}") {
    val id = call.parameters["id"]?.toLongOrNull() ?: return@get call.respond(HttpStatusCode.BadRequest)
    userService.findById(id).fold(
        ifLeft  = { call.respond(HttpStatusCode.NotFound) },
        ifRight = { call.respond(it) }
    )
}

Quick Check

What does flatMap { } do on an Either.Left value?

Recap: Either<L, R>

Key takeaways:

  • Either makes the failure path explicit in the function signature
  • Right = success; Left = failure (by convention)
  • map { } — transform the success value; flatMap { } — chain fallible computations
  • fold(ifLeft, ifRight) — consume both cases in one expression
  • Either.catch { } — wrap exception-throwing code at boundaries
Free to start

Learn Kotlin with an AI tutor — free

Write and run real code in your browser, get instant help from a 24/7 AI tutor, and pick up where you left off on the web or in the app.

Courses
51
Lessons
203

Frequently Asked Questions

Is the “Either<L, R>: Typed Error Handling Without Exceptions” lesson free?

Yes — any 3 lessons of the Kotlin Academy learning path, including “Either<L, R>: Typed Error Handling Without Exceptions”, are free to read in full here on the web. After that, CoddyKit PRO unlocks every lesson, plus interactive practice with a built-in code editor and a 24/7 AI tutor. The Kotlin Academy course includes 4 lessons in total.

What will I learn in “Either<L, R>: Typed Error Handling Without Exceptions”?

Use Either to represent success and failure without throwing exceptions. You practise Kotlin Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Kotlin Academy?

No prior experience is required. Kotlin Academy on CoddyKit is structured for beginners through advanced learners, so you can start here or from the beginning and move at your own pace. This is lesson 1 of 4.

How long does the “Either<L, R>: Typed Error Handling Without Exceptions” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Kotlin Academy lesson?

Yes. Every Kotlin Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Either : Typed Error Handling Without Exceptions
  2. Arrow Raise DSL: Composing Typed Errors
  3. Option and Nullable: When to Use Each
  4. Functional Domain Modeling with Arrow's Core Types
← Back to Kotlin Academy