DSL Arrow Raise: объединение типизированных ошибок
Используйте контекст Raise в Arrow и raise(), чтобы аккуратно объединять потоки с несколькими ошибками
«DSL Arrow Raise: объединение типизированных ошибок» — бесплатный урок Kotlin Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Kotlin Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Kotlin Academy содержит 4 уроков всего.
Ограничения цепочек Either
Цепочка Either с помощью flatMap приводит к глубоко вложенным лямбдам при наличии нескольких последовательных операций. DSL Raise Arrow (появившийся в Arrow 1.2) предлагает более чистый синтаксис в стиле сопрограмм с использованием контекстных получателей Kotlin или функций-расширений.
Что такое Raise<E>
Raise — это контекст, который может поднять (завершить вычисление досрочно) ошибку типа E. Функция, которая может завершиться ошибкой, вызывает raise(error), чтобы прервать выполнение и передать ошибку дальше — подобно выбрасыванию исключения, но без исключений.
Построитель either { }
Построитель either { } создаёт Either<E, A> из блока, в области видимости которого находится Raise<E>. Внутри вызовите raise(error) при ошибке или просто верните значение при успехе:
import arrow.core.raise.either
import arrow.core.raise.Raise
fun validateAge(age: Int): Either<String, Int> = either {
if (age < 0) raise("Age cannot be negative")
if (age > 150) raise("Age too large")
age
}bind() — распаковка Either внутри either{}
Внутри блока either { } вызовите .bind() для любого значения Either, чтобы распаковать его. Если это Left, bind() автоматически поднимает ошибку и досрочно завершает блок:
fun createUser(name: String, age: Int): Either<String, User> = either {
val validName = validateName(name).bind() // raises if Left
val validAge = validateAge(age).bind() // raises if Left
User(validName, validAge) // only reached if both succeed
}ensure() — встроенная проверка условия
ensure(condition) { error } — это сокращённая форма записи if (!condition) raise(error). Она позволяет сделать логику проверки краткой:
fun validateEmail(email: String): Either<String, String> = either {
ensure(email.contains("@")) { "Email must contain @" }
ensure(email.length <= 255) { "Email too long" }
email
}ensureNotNull() — проверка null с помощью Raise
ensureNotNull(value) { error } распаковывает значение, допускающее null, или поднимает ошибку, если значение равно null:
fun findUser(id: Long, repo: UserRepo): Either<UserError, User> = either {
ensureNotNull(repo.findById(id)) { UserError.NotFound(id) }
}Raise с контекстными получателями (Arrow 1.2+)
Можно объявлять функции, которым непосредственно требуется контекст Raise<E>, без оборачивания в either { }:
context(Raise<String>)
fun requirePositive(n: Int): Int {
ensure(n > 0) { "Must be positive" }
return n
}
// Called inside either { }
val result = either { requirePositive(-1) } // Left("Must be positive")Накопление ошибок с помощью zipOrAccumulate
По умолчанию Raise завершает выполнение при первой ошибке. Используйте zipOrAccumulate(), чтобы выполнить несколько проверок и собрать все ошибки в NonEmptyList:
val result: Either<NonEmptyList<String>, User> = either {
zipOrAccumulate(
{ validateName(name).bind() },
{ validateAge(age).bind() }
) { validName, validAge -> User(validName, validAge) }
}recover { } — обработка определённых ошибок
Используйте recover { error -> ... } внутри either { }, чтобы обработать конкретную поднятую ошибку и предоставить резервное значение, продолжив вычисление:
val name: String = either {
findUser(0L).bind().name
}.recover { error ->
if (error == UserError.NotFound) "Guest" else raise(error)
}.getOrNull() ?: "Guest"Raise и цепочки Either: когда что использовать
Используйте either { }.bind() для линейных потоков с несколькими последовательными шагами — такой код читается как императивный, но остаётся чистым. Используйте цепочки flatMap для коротких композиций из двух шагов или при объединении с другими функциональными библиотеками.
Тестирование функций на основе Raise
Проверяйте результат, вызывая функцию внутри either { } и выполняя проверку. Сопоставители Kotest shouldBeLeft() и shouldBeRight() делают утверждения краткими:
import arrow.core.shouldBeLeft
import arrow.core.shouldBeRight
@Test
fun `negative age returns Left`() {
validateAge(-1).shouldBeLeft()
}
@Test
fun `valid age returns Right`() {
validateAge(25).shouldBeRight(25)
}Быстрая проверка
Что происходит при вызове .bind() для значения Either.Left внутри блока either { }?
Повторение: DSL Raise Arrow
Основные выводы:
either { }создаётEitherиз блока, в области видимости которого находитсяRaise<E>.bind()распаковываетEither; при значенииLeftзавершает выполнение досрочноensure(condition) { error }иensureNotNull(value) { error }предназначены для встроенных проверокzipOrAccumulate()собирает несколько ошибок вместо досрочного завершения- Код читается как императивный, оставаясь чисто функциональным
Часто задаваемые вопросы
Урок «DSL Arrow Raise: объединение типизированных ошибок» бесплатный?
Да — полный текст урока «DSL Arrow Raise: объединение типизированных ошибок» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Kotlin Academy, подпишись на CoddyKit PRO. Курс Kotlin Academy содержит 4 уроков всего.
Чему я научусь в уроке «DSL Arrow Raise: объединение типизированных ошибок»?
Используйте контекст Raise в Arrow и raise(), чтобы аккуратно объединять потоки с несколькими ошибками Ты практикуешь Kotlin Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Kotlin Academy?
Предыдущий опыт не требуется. Kotlin Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.
Сколько времени занимает урок «DSL Arrow Raise: объединение типизированных ошибок»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Kotlin Academy?
Да. Каждый урок Kotlin Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Either : типизированная обработка ошибок без исключений
- DSL Arrow Raise: объединение типизированных ошибок
- Option и Nullable: когда использовать каждый из них
- Функциональное моделирование предметной области с базовыми типами Arrow