3-19.【函数式编程】如何用 Result 类型实现函数式错误处理?

4 阅读2分钟

1️⃣ Result 类型简介

Swift 的 Result 是一个泛型枚举:

enum Result<Success, Failure: Error> {
    case success(Success)
    case failure(Failure)
}
  • success 包含成功值
  • failure 包含错误
  • 可用 mapflatMapmapError 等方法进行组合和转换
  • 支持纯函数式链式处理,无需显式 try/catch

2️⃣ 使用 Result 的基本示例

假设有一个字符串转 Int 的函数,可能失败:

enum ParseError: Error {
    case invalidNumber
}

// 返回 Result 类型
func parseInt(_ str: String) -> Result<Int, ParseError> {
    if let n = Int(str) {
        return .success(n)
    } else {
        return .failure(.invalidNumber)
    }
}

// 使用
let result = parseInt("123")
switch result {
case .success(let value):
    print("Parsed value:", value)
case .failure(let error):
    print("Error:", error)
}
  • 核心逻辑纯函数
  • 错误封装在 Result 中,而不是抛出副作用

3️⃣ 链式函数式处理

Result 支持 map / flatMap,可以安全组合多个可能失败的操作:

func increment(_ x: Int) -> Result<Int, ParseError> {
    return .success(x + 1)
}

func double(_ x: Int) -> Result<Int, ParseError> {
    return .success(x * 2)
}

// 流水线组合
let processed = parseInt("10")
    .flatMap(increment)  // 输入 Result<Int, Error> -> 输出 Result<Int, Error>
    .flatMap(double)

switch processed {
case .success(let value):
    print("Result:", value) // Result: 22
case .failure(let error):
    print("Error:", error)
}

优势

  • 链式操作可读性强
  • 错误自动传递,无需 try/catch
  • 保持函数式风格,核心逻辑纯净

4️⃣ mapError 处理错误

如果想在链式中转换错误类型,也可使用 mapError

enum AppError: Error {
    case parseFailed
}

let finalResult = parseInt("abc")  // 会失败
    .mapError { _ in AppError.parseFailed }

switch finalResult {
case .success(let value):
    print("Value:", value)
case .failure(let error):
    print("Mapped Error:", error) // Mapped Error: parseFailed
}
  • 可以统一错误类型,便于应用逻辑处理

5️⃣ 数组或集合上的函数式处理

可以结合 Result 和数组的 map / compactMap 处理多个可能失败的元素:

let inputs = ["1", "2", "three", "4"]

let results: [Result<Int, ParseError>] = inputs.map(parseInt)

// 只保留成功值
let successes = results.compactMap {
    if case let .success(value) = $0 { return value }
    else { return nil }
}

print(successes) // [1, 2, 4]
  • 保持函数式流水线
  • 错误不会中断整个流程
  • 可选择集中处理失败或忽略失败

6️⃣ 优势总结

特性优势
链式组合 (map / flatMap)可组合多个可能失败的操作
错误封装无需抛出副作用,核心逻辑纯净
可测试输入输出确定,易单元测试
与数组/集合组合可函数式处理批量操作,灵活处理失败

💡 经验法则

使用 Result 封装可能失败的操作 → 链式函数组合 → 错误统一处理 → 保持函数式、纯函数逻辑