Either — функционален тип, който представлява стойността на един от два възможни варианта: ляв (Left) за грешка и десен (Right) за успех. За разлика от изключенията, Either прави обработката на грешки изрична на ниво типове и не изисква блокове try-catch. Според данни на Arrow, 2026, Either се използва широко в Kotlin проекти за композиране на операции, които могат да завършат с неуспех, без странични ефекти.
Основни точки
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 може да бъде само един от тях във всеки момент. Типичният подпис в Kotlin изглежда като Either<E, A>, където E е типът на грешката, A — типът на успешната стойност.
Left представлява случай на повреда. За разлика от изключенията, Left не прекъсва потока на изпълнение — той просто връща стойност, която трябва да бъде обработена. Типът на грешката може да бъде всякакъв: String, Int, персонализиран sealed class или домейн модел.
Right съдържа правилния резултат от операцията. Името отразява „правилност" — по конвенция Right означава успех. Именно върху Right се прилагат трансформациите map и flatMap, което позволява изграждането на вериги от изчисления без проверка на всяка стъпка.
flatMap — основният механизъм за композиция на Either. Ако текущата стойност е Right, flatMap прилага подадената функция и връща нов Either. Ако е Left — flatMap пропуска трансформацията и предава грешката нататък. Това поведение се нарича short-circuit evaluation.
Освен flatMap, Either поддържа mapLeft за трансформация на грешка, fold за обработка на двата варианта на едно място и getOrElse за извличане на стойност с подразбираща се. Тези функции покриват всички сценарии: от просто извличане до сложна композиция с асинхронни извиквания в корутини на Kotlin или Combine в Swift.
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) }
В екосистемата на Kotlin Either е имплементиран в библиотеката Arrow. Стандартната библиотека на Kotlin предлага Result, но Either дава повече гъвкавост: произволен тип грешка, композиция чрез flatMap и поддръжка на функционални модели.
Arrow — функционална библиотека за Kotlin, която добавя Either, Option, Validated и други типове. Arrow.Either — sealed class с два наследника: ArrowCore.Left и ArrowCore.Right. Библиотеката също предоставя разширения за удобна работа: .getOrElse(), .fold(), .mapLeft().
Нека разгледаме реален сценарий — мрежова заявка в Android приложение с възможни грешки: липса на мрежа, грешка на сървър, невалиден отговор. Either позволява обединяването на всички варианти в един връщан тип.
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 ?: ""))
}
}
В Swift от версия 5.0 се появи вграденият тип Result, който концептуално прилича на Either, но има ограничения: грешката трябва да съответства на протокола Error, а успешната стойност — на един тип. Either в Swift се имплементира чрез enum с два генерични параметъра.
Result<Success, Failure> — вграденият тип на Swift, където Failure: Error. Either не налага ограничения върху типа грешка, което позволява съхраняването на String, персонализирани структури или дори няколко типа грешки чрез вложени enum.
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 е оптимален за сценарии, които изискват изрична и типобезопасна обработка на грешки без изключения. Нека разгледаме основните случаи на приложение в мобилното разработване на Kotlin и Swift.
Не използвайте Either за прости операции без странични ефекти — обикновената върната стойност е по-надеждна и по-лесна за четене. Either е също излишен, когато повредата е изключителна ситуация, а не очакван сценарий.
Either е също ефективен при работа с корутини в Kotlin. Функция, която връща Either, може да бъде извикана вътре в корутина с обработка на грешка чрез fold или mapLeft без блокиране на нишката. Това е особено полезно в Android приложения с MVVM архитектура, където всяко хранилище връща Either, а ViewModel трансформира резултата в UiState.
Разработчиците, които за първи път се запознават с Either, често допускат подобни грешки. Нека разгледаме най-честите от тях и начините за избягването им.
Често задавани въпроси
Optional представлява наличие или отсъствие на стойност (Some/None), но не съобщава причината за отсъствието. Either дава два конкретни типа — ляв за грешка и десен за успех, което позволява предаване на контекста на повредата.
В Java няма вграден Either, но библиотеките Vavr и functionaljava предоставят имплементация. В Android разработката Either от Vavr е популярна алтернатива за функционален стил с ламбда изрази.
Sealed class в Kotlin е по-удобен, когато вариантите са повече от два или имат различна структура. За бинарен изход (грешка/успех) Either е по-компактен и предлага готови функционални комбинатори.
Either е неизменяем и безопасен за нишки по подразбиране. В Kotlin с корутини Either се комбинира отлично: flatMap работи вътре в coroutine scope, а обработката на грешки не изисква блокировки.
Не. Either е подходящ за операции с очаквани повреди (мрежа, валидация, бизнес логика). За прости getter-и и изчисления без странични ефекти, обикновеният тип е по-лесен за четене и не добавя излишна сложност.
Обобщение
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също