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 із двома generic-параметрами.
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 доречний для операцій з очікуваними збоями (мережа, валідація, бізнес-логіка). Для простих геттерів і обчислень без побічних ефектів звичайний тип читається простіше та не додає зайвої складності.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також