Arrow Raise DSL:组合类型化错误
使用 Arrow 的 Raise 上下文和 raise(),简洁地组合多错误流程。
Arrow Raise DSL:组合类型化错误 是 CoddyKit 上的免费 Kotlin Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Kotlin Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Kotlin Academy 课程共包含 4 节课。
Either 链式调用的局限
当有多个连续操作时,使用 flatMap 链式调用会产生层层嵌套的 lambda。Arrow 的 Raise DSL(在 Arrow 1.2 中引入)使用 Kotlin 的上下文接收器或扩展函数,提供了更简洁的协程风格语法。
什么是 Raise<E>
Raise 是一种上下文,可以 引发 类型为 E 的错误并提前结束计算。可能失败的函数可以调用 raise(error) 来中止并传播错误,类似于抛出异常,但不使用异常。
either { } 构建器
either { } 构建器会根据一个处于 Raise 作用域中的代码块创建 Either。在代码块内部,您可以通过 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 { } 代码块中,可以对任何 Either 值调用 .bind() 来解包。如果它是 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() — 使用 Raise 检查空值
ensureNotNull(value) { error } 会解包可空值;如果值为 null,则引发错误:
fun findUser(id: Long, repo: UserRepo): Either<UserError, User> = either {
ensureNotNull(repo.findById(id)) { UserError.NotFound(id) }
}使用上下文接收器进行 Raise(Arrow 1.2+)
您可以直接声明要求 Raise 上下文的函数,而无需将其包裹在 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 { } — 处理特定错误
在 either { } 内使用 recover { error -> ... } 处理特定的引发错误,并提供回退值以继续计算:
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 { } 中调用函数并断言结果。Arrow 的 shouldBeLeft() 和 shouldBeRight() Kotest 匹配器可以让断言更加简洁:
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)
}快速检查
在 either { } 代码块中,对 Either.Left 值调用 .bind() 会发生什么?
回顾:Arrow Raise DSL
关键要点:
either { }会根据处于Raise作用域中的代码块创建Either.bind()会解包Either;遇到Left时提前结束- 使用
ensure(condition) { error }和ensureNotNull(value) { error }进行内联检查 - 使用
zipOrAccumulate()收集多个错误,而不是提前结束 - 读起来像命令式代码,同时保持纯函数式
常见问题解答
「Arrow Raise DSL:组合类型化错误」课时是免费的吗?
是的 — 「Arrow Raise DSL:组合类型化错误」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Kotlin Academy 课程的其余内容,请升级到 CoddyKit PRO。 Kotlin Academy 课程共包含 4 节课。
「Arrow Raise DSL:组合类型化错误」这节课中我会学到什么?
使用 Arrow 的 Raise 上下文和 raise(),简洁地组合多错误流程。 你通过在浏览器中直接运行的动手代码来练习 Kotlin Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Kotlin Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Kotlin Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。
「Arrow Raise DSL:组合类型化错误」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Kotlin Academy 课中编写并运行代码吗?
能。每节 Kotlin Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- Either :无需异常的类型化错误处理
- Arrow Raise DSL:组合类型化错误
- Option 与 Nullable:如何选择
- 使用 Arrow 核心类型进行函数式领域建模