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 因 Kotlin 的 Arrow 库和 Swift 中的函数式方法而广受欢迎。

Either 相对于异常的主要优势 — 没有隐藏的执行路径。返回 Either 的函数在其签名中显式声明可能以错误结束。编译器控制是否处理了两种选项。

与抛出异常不同,Either 保持 数据流的透明性。调用返回 Either 的函数不需要在调用代码侧使用 try-catch — pattern-matching 或 fold 就足够了。这在采用响应式方法的架构中尤其重要,其中每个数据源返回 Either,转换链通过 map 和 flatMap 构建。

Either 如何工作:Left 和 Right 的结构

Either 由两个子类型组成:LeftRight。一个 Either 实例在每一时刻只能是其中之一。Kotlin 中的典型签名是 Either<E, A>,其中 E 是错误类型,A — 成功值的类型。

Left 类型:错误容器

Left 表示故障情况。与异常不同,Left 不会中断执行流 — 它只是返回一个需要处理的值。错误类型可以是任何类型:StringInt、自定义 sealed class 或领域模型。

Right 类型:成功容器

Right 包含操作的正确结果。名称反映了 “正确性” — 按照约定,Right 意味着成功。正是在 Right 上应用 map 和 flatMap 转换,从而无需检查每一步即可构建计算链。

通过 flatMap 组合 Either

flatMap — Either 组合的主要机制。如果当前值是 Right,flatMap 应用传入的函数并返回新的 Either。如果是 Left — flatMap 跳过转换并将错误传递下去。这种行为称为 short-circuit evaluation。

除了 flatMap,Either 还支持 mapLeft(用于转换错误)、fold(用于在同一个地方处理两种选项)和 getOrElse(用于提取带有默认值的值)。这些函数涵盖了所有场景:从简单的提取到 Kotlin 协程或 Swift 的 Combine 中的异步调用复杂组合。

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) }

Kotlin 中的 Either:实际应用

Kotlin 生态系统中,Either 在 Arrow 库中实现。Kotlin 标准库提供 Result,但 Either 提供更大的灵活性:任意的错误类型、通过 flatMap 组合以及对函数式模式的支持。

Arrow 库和 Either

Arrow — 一个为 Kotlin 增加 Either、Option、Validated 等类型的函数式库。Arrow.Either — 具有两个继承者的 sealed class:ArrowCore.LeftArrowCore.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 ?: ""))
    }
}

Swift 中的 Either:与标准 Result 的区别

Swift 中,从 5.0 版本开始出现了内置的 Result 类型,它在概念上类似于 Either,但有局限性:错误必须符合 Error 协议,成功值必须是一种类型。Swift 中的 Either 通过具有两个泛型参数的 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 处理错误,而无需阻塞线程。这在具有 MVVM 架构的 Android 应用中尤其有用,其中每个仓库返回 Either,ViewModel 将结果转换为 UiState。

使用 Either 时的常见错误

初次接触 Either 的开发人员经常犯类似的错误。让我们看看最常见的错误及其避免方法。

  • 忽略 Left — 调用 .getOrElse() 并传入默认值而不分析错误原因,使 Either 的使用失去意义。显式处理错误。
  • 全局错误类型 — 在项目中将 String 或 Exception 用作所有 Either 的共同类型会降低类型安全性。创建领域 sealed class。
  • 嵌套 Either — Right 内部重复的 Either(Either<E, Either<E, A>>)使阅读变得困难。使用 flatMap 进行扁平化。
  • 与异常混合 — 函数返回 Either,但内部抛出异常。这与显式错误处理的思想相矛盾。

常见问题

Either 与 Optional 有何不同?

Optional 表示值的存在或缺失(Some/None),但不说明缺失的原因。Either 提供两个具体的类型 — 左用于错误,右用于成功,从而允许传递故障的上下文。

可以在 Java 中使用 Either 吗?

Java 中没有内置的 Either,但 Vavr 和 functionaljava 库提供了实现。在 Android 开发中,来自 Vavr 的 Either 是使用 lambda 的函数式风格的流行替代方案。

何时使用 sealed class 而不是 Either?

当选项超过两个或具有不同结构时,Kotlin 中的 Sealed class 更方便。对于二元结果(错误/成功),Either 更紧凑并提供现成的函数式组合器。

Either 支持多线程吗?

Either 是不可变的,默认是线程安全的。在 Kotlin 中与协程配合时,Either 完美结合:flatMap 在 coroutine scope 内工作,错误处理不需要锁定。

应该为项目中的所有函数使用 Either 吗?

不。Either 适用于预期会失败的操作(网络、验证、业务逻辑)。对于没有副作用的简单 getter 和计算,普通类型更易读且不会增加不必要的复杂性。

总结

  • Either — 用于表示两种选项的函数式类型:Left(错误)和 Right(成功),在类型层面显式处理故障。
  • Left 可以包含任意的错误类型 — 从 String 到领域 sealed class,不同于标准 Result。
  • flatMap 确保 Without 嵌套检查的 Either 组合:错误自动在整个链中传播。
  • Arrow — Kotlin 的主要 Either 库,内置对协程和扩展的支持。
  • Swift 使用内置的 Result,但 Either 通过具有两个泛型参数的 enum 实现以提供完全的灵活性。
  • 将 Either 用于 网络请求、表单验证和数据仓库 — 预期出现错误的场景。
  • 避免 全局错误类型、忽略 Left 和嵌套 Either — 这会降低类型安全性的优势。

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读