Either: суть, применение и принцип работы

Автор: IT Sectr Опубликовано: 2026-05-26 Время чтения: 8 мин

Either — функциональный тип, который представляет значение одного из двух возможных вариантов: левый (Left) для ошибки и правый (Right) для успеха. В отличие от исключений, Either делает обработку ошибок явной на уровне типов и не требует блоков try-catch. По данным Arrow, 2026, Either широко применяется в Kotlin-проектах для композиции операций, которые могут завершиться сбоем, без побочных эффектов.

Главное

  • Either — алгебраический тип данных для представления двух взаимоисключающих вариантов: Left (ошибка) и Right (успех).
  • Left традиционно содержит информацию об ошибке, Right — корректное значение.
  • Явная типизация — Either заставляет обрабатывать оба варианта на этапе компиляции, исключая неожиданные сбои.
  • Композиция — Either поддерживает map, flatMap и fold для цепочек преобразований без вложенных проверок.
  • Отличие от Result — Either может хранить произвольный тип ошибки, а не только Error или Throwable.

Что такое Either?

Either — это алгебраический тип данных, заимствованный из функционального программирования, который представляет значение ровно одного из двух возможных типов. В контексте обработки ошибок принято соглашение: левый тип (Left) содержит описание проблемы, а правый (Right) — успешный результат.

Концепция Either восходит к языку Haskell и категории типов Either a b, где a — тип левого значения, b — правого. В мобильной разработке Either получил распространение благодаря библиотеке Arrow для Kotlin и функциональным подходам в Swift.

Ключевое преимущество Either перед исключениями — отсутствие скрытых путей выполнения. Функция, возвращающая Either, явно заявляет в своей сигнатуре, что может завершиться ошибкой. Компилятор контролирует, обработаны ли оба варианта.

В отличие от выбрасывания исключений, Either сохраняет прозрачность потока данных. Вызов функции, возвращающей Either, не требует try-catch на стороне вызывающего кода — достаточно pattern-matching или fold. Это особенно важно в архитектурах с реактивным подходом, где каждый источник данных возвращает Either, а цепочка трансформаций строится через map и flatMap.

Как работает Either: структура Left и Right

Either состоит из двух подтипов: Left и Right. Экземпляр Either может быть только одним из них в каждый момент времени. Типовая сигнатура в Kotlin выглядит как Either<E, A>, где E — тип ошибки, A — тип успешного значения.

Тип Left: контейнер ошибки

Left представляет случай сбоя. В отличие от исключений, Left не прерывает поток выполнения — он просто возвращает значение, которое нужно обработать. Тип ошибки может быть любым: String, Int, кастомный sealed class или доменная модель.

Тип Right: контейнер успеха

Right содержит корректный результат операции. Название отражает «правильность» — по соглашению Right означает успех. Именно на Right применяются трансформации map и flatMap, что позволяет строить цепочки вычислений без проверки каждого шага.

Композиция Either через flatMap

flatMap — основной механизм композиции Either. Если текущее значение — Right, flatMap применяет переданную функцию и возвращает новый Either. Если Left — flatMap пропускает преобразование и передаёт ошибку дальше. Это поведение называется short-circuit evaluation.

Помимо flatMap, Either поддерживает mapLeft для трансформации ошибки, fold для обработки обоих вариантов в одном месте и getOrElse для извлечения значения с дефолтом. Эти функции покрывают все сценарии: от простого извлечения до сложной композиции с асинхронными вызовами в корутинах Kotlin или Combine в Swift.

kotlin
fun parseInt(input: String): Either<String, Int> =
    input.toIntOrNull()?.let { Right(it) }
    ?: Left("Не удалось преобразовать: $input")

fun divide(a: Int, b: Int): Either<String, Int> =
    if (b == 0) Left("Деление на ноль")
    else Right(a / b)

val result = parseInt("10")
    .flatMap { divide(it, 2) }

Either в Kotlin: практическое применение

В экосистеме Kotlin Either реализован в библиотеке Arrow. Стандартная библиотека Kotlin предлагает Result, но Either даёт больше гибкости: произвольный тип ошибки, композиция через flatMap и поддержка функциональных паттернов.

Библиотека Arrow и Either

Arrow — функциональная библиотека для Kotlin, которая добавляет Either, Option, Validated и другие типы. Arrow.Either — sealed class с двумя наследниками: ArrowCore.Left и ArrowCore.Right. Библиотека также предоставляет расширения для удобной работы: .getOrElse(), .fold(), .mapLeft().

Пример: Either с сетевым запросом

Рассмотрим реальный сценарий — сетевой запрос в Android приложении с возможными ошибками: отсутствие сети, ошибка сервера, некорректный ответ. Either позволяет объединить все варианты в одном возвращаемом типе.

kotlin
sealed class NetworkError {
    data class NoConnection(val message: String): NetworkError()
    data class ServerError(val code: Int): NetworkError()
    data class ParseError(val detail: String): NetworkError()
}

suspend fun fetchUser(id: String): Either<NetworkError, User> {
    return try {
        val response = api.getUser(id)
        if (response.isSuccessful) {
            Right(response.body()!!)
        } else {
            Left(NetworkError.ServerError(response.code()))
        }
    } catch (e: IOException) {
        Left(NetworkError.NoConnection(e.message ?: ""))
    }
}

Either в Swift: отличие от стандартного Result

В Swift начиная с версии 5.0 появился встроенный Result тип, который концептуально похож на Either, но имеет ограничения: ошибка должна соответствовать протоколу Error, а успешное значение — одному типу. Either в Swift реализуется через enum с двумя generic-параметрами.

Стандартный Result против Either

Result<Success, Failure> — встроенный тип Swift, где Failure: Error. Either не накладывает ограничений на тип ошибки, что позволяет хранить String, кастомные структуры или даже несколько типов ошибок через вложенные enum.

swift
enum Either<E, A> {
    case left(E)
    case right(A)

    func map<B>(_ transform: (A) -> B) -> Either<E, B> {
        switch self {
        case .left(let e): return .left(e)
        case .right(let a): return .right(transform(a))
        }
    }
}

let result: Either<String, Int> = .right(42)
let mapped = result.map { $0 * 2 }

Когда использовать Either в мобильных приложениях

Either оптимален для сценариев, где требуется явная и типобезопасная обработка ошибок без исключений. Рассмотрим основные случаи применения в мобильной разработке на Kotlin и Swift.

  • Сетевые запросы — каждый запрос может вернуть ошибку соединения, сервера или парсинга. Either собирает все варианты в один тип.
  • Валидация форм — Either с кастомным типом ошибки удобно представляет результат проверки полей ввода: невалидный email или пароль.
  • Репозиторий данных — Either позволяет объединить результат из разных источников (кеш, БД, сеть) с единым типом ошибки.
  • Интерпретатор команд — если приложение разбирает пользовательский ввод или команды, Either даёт типобезопасный способ сообщить об ошибке.

Не стоит использовать Either для простых операций без побочных эффектов — обычное возвращаемое значение надёжнее и читается легче. Также Either избыточен, когда сбой — это исключительная ситуация, а не ожидаемый сценарий.

Either также эффективен при работе с корутинами в Kotlin. Функция, возвращающая Either, может быть вызвана внутри корутины с обработкой ошибки через fold или mapLeft без блокировки потока. Это особенно полезно в Android-приложениях с архитектурой MVVM, где каждый репозиторий возвращает Either, а ViewModel трансформирует результат в UiState.

Типичные ошибки при работе с Either

Разработчики, впервые знакомящиеся с Either, часто допускают схожие ошибки. Рассмотрим наиболее распространённые и способы их избежать.

  • Игнорирование Left — вызов .getOrElse() с дефолтным значением без анализа причины ошибки лишает смысла использование Either. Обрабатывайте ошибку явно.
  • Глобальный тип ошибки — использование String или Exception как общего типа для всех Either в проекте снижает типобезопасность. Создавайте доменные sealed class.
  • Вложенные Either — повторный Either внутри Right (Either<E, Either<E, A>>) усложняет чтение. Используйте flatMap для выравнивания.
  • Смешивание с исключениями — функция возвращает Either, но внутри выбрасывает исключение. Это противоречит идее явной обработки ошибок.

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

Чем Either отличается от Optional?

Optional представляет наличие или отсутствие значения (Some/None), но не сообщает причину отсутствия. Either даёт два конкретных типа — левый для ошибки и правый для успеха, что позволяет передавать контекст сбоя.

Можно ли использовать Either в Java?

В Java нет встроенного Either, но библиотеки Vavr и functionaljava предоставляют реализацию. В Android-разработке Either из Vavr — популярная альтернатива для функционального стиля с лямбдами.

Когда лучше использовать sealed class вместо Either?

Sealed class в Kotlin удобнее, когда вариантов больше двух или они имеют разную структуру. Для бинарного исхода (ошибка/успех) Either компактнее и предоставляет готовые функциональные комбинаторы.

Поддерживает ли Either многопоточность?

Either иммутабелен и потокобезопасен по умолчанию. В Kotlin с корутинами Either отлично комбинируется: flatMap работает внутри coroutine scope, а обработка ошибок не требует блокировок.

Стоит ли использовать Either для всех функций в проекте?

Нет. Either уместен для операций с ожидаемыми сбоями (сеть, валидация, бизнес-логика). Для простых геттеров и вычислений без побочных эффектов обычный тип читается проще и не добавляет лишней сложности.

Итоги

  • Either — функциональный тип для представления двух вариантов: Left (ошибка) и Right (успех), делающий обработку сбоев явной на уровне типов.
  • Left может содержать произвольный тип ошибки — от String до доменного sealed class, в отличие от стандартного Result.
  • flatMap обеспечивает композицию Either без вложенных проверок: ошибка автоматически пробрасывается через всю цепочку.
  • Arrow — основная библиотека Either для Kotlin со встроенной поддержкой корутин и расширений.
  • Swift использует встроенный Result, но Either реализуется через enum с двумя generic-параметрами для полной гибкости.
  • Применяйте Either для сетевых запросов, валидации форм и репозиториев — сценариев с ожидаемыми ошибками.
  • Избегайте глобального типа ошибки, игнорирования Left и вложенных Either — это снижает преимущества типобезопасности.

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также