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 с два генерични параметъра.

Стандартен 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 с персонализиран тип грешка удобно представя резултата от проверката на входните полета: невалиден имейл или парола.
  • Репозиториум за данни — 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 е подходящ за операции с очаквани повреди (мрежа, валидация, бизнес логика). За прости getter-и и изчисления без странични ефекти, обикновеният тип е по-лесен за четене и не добавя излишна сложност.

Обобщение

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

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

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също