Kotlin Academy · Урок

Either<L, R>: типизированная обработка ошибок без исключений

Используйте Either, чтобы представлять успешное и неудачное выполнение без выбрасывания исключений

Урок 1 из 413 шагов

«Either<L, R>: типизированная обработка ошибок без исключений» — бесплатный урок Kotlin Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Kotlin Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Kotlin Academy содержит 4 уроков всего.

Проблема использования исключений в бизнес-логике

Исключения предназначены для неожиданных сбоев (нулевой указатель, ошибка IO). Использование их для обозначения ожидаемых ошибок бизнес-логики (ошибка валидации, «не найдено») делает поток управления невидимым, вынуждает добавлять try/catch в месте каждого вызова и скрывает ошибки в сигнатурах функций.

Что такое Either<L, R>

Either — это тип-сумма с двумя вариантами: Left(value: L) по соглашению содержит ошибку, а Right(value: R) — значение успешного результата. Функция, возвращающая Either, явно указывает возможность сбоя в своей сигнатуре.

Either в Arrow

Добавьте Arrow в свой проект, чтобы получить Either и всю экосистему его операторов:

// 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

Возврат Either из функции

Возвращайте value.right() при успехе и error.left() при ошибке. Вызывающий код должен обработать обе ветви:

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()

Обработка Either с помощью fold

Используйте fold(ifLeft, ifRight), чтобы обработать оба варианта в одном выражении:

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

Преобразование значения Right с помощью map

map { } преобразует значение Right, не затрагивая Left. Благодаря этому Either является функтором — преобразования можно безопасно объединять в цепочку:

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

Объединение в цепочку с помощью flatMap

flatMap { } объединяет в цепочку вычисления, которые сами возвращают Either. Если какой-либо шаг возвращает Left, цепочка завершается досрочно, а ошибка передаётся дальше без выполнения последующих шагов:

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 для оборачивания исключений

Either.catch { } выполняет блок и оборачивает любое исключение в Left<Throwable>. Используйте его на границе между устаревшим кодом, который выбрасывает исключения, и доменом на основе Either:

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

Восстановление с помощью getOrElse и orElse

getOrElse { default } извлекает значение Right или возвращает значение по умолчанию. orElse { alternativeEither } заменяет Left другим вычислением:

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

Сопоставление с образцом с помощью when

Используйте when для запечатанного класса, подобного Either, или непосредственно для Either с помощью is Either.Left / Either.Right, чтобы исчерпывающе обработать все варианты:

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

Either в сервисных слоях

Возвращайте Either из функций репозитория и сервиса. Преобразуйте его в HTTP-ответы на уровне маршрута Ktor, сохраняя бизнес-логику свободной от деталей фреймворка:

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) }
    )
}

Быстрая проверка

Что делает flatMap { } со значением Either.Left?

Повторение: Either<L, R>

Основные выводы:

  • Either явно указывает путь обработки ошибки в сигнатуре функции
  • Right = успех; Left = ошибка (по соглашению)
  • map { } — преобразовать успешное значение; flatMap { } — объединить в цепочку вычисления, которые могут завершиться ошибкой
  • fold(ifLeft, ifRight) — обработать оба варианта в одном выражении
  • Either.catch { } — обернуть код, выбрасывающий исключения, на границах системы
Можно начать бесплатно

Изучай Kotlin с ИИ-репетитором — бесплатно

Пиши и запускай код прямо в браузере, получай мгновенную помощь от ИИ-репетитора 24/7 и продолжи учиться на сайте или в приложении.

Курсы
51
Уроки
203

Часто задаваемые вопросы

Урок «Either<L, R>: типизированная обработка ошибок без исключений» бесплатный?

Да — полный текст урока «Either<L, R>: типизированная обработка ошибок без исключений» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Kotlin Academy, подпишись на CoddyKit PRO. Курс Kotlin Academy содержит 4 уроков всего.

Чему я научусь в уроке «Either<L, R>: типизированная обработка ошибок без исключений»?

Используйте Either, чтобы представлять успешное и неудачное выполнение без выбрасывания исключений Ты практикуешь Kotlin Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Kotlin Academy?

Предыдущий опыт не требуется. Kotlin Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.

Сколько времени занимает урок «Either<L, R>: типизированная обработка ошибок без исключений»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Kotlin Academy?

Да. Каждый урок Kotlin Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Either : типизированная обработка ошибок без исключений
  2. DSL Arrow Raise: объединение типизированных ошибок
  3. Option и Nullable: когда использовать каждый из них
  4. Функциональное моделирование предметной области с базовыми типами Arrow
← Назад к Kotlin Academy