3-28.【函数式编程】设计一个泛型函数,将任意 Monad 类型组合多个操作,要求类型安全并保持错误传播。

4 阅读2分钟

1️⃣ 定义 Monad 协议

// 定义一个泛型 Monad 协议
protocol Monad {
    associatedtype Value
    associatedtype ErrorType: Error

    // bind / flatMap
    func flatMap<B>(_ transform: (Value) -> Self) -> Self
}

Swift 的类型系统无法直接统一 OptionalResult,所以我们可以针对 ResultOptional 写泛型函数。


2️⃣ 泛型函数:链式组合

设计一个函数:

  • 接收一个初始 Monad 值
  • 接收一个 操作数组,每个操作是 (A) -> Monad<A>
  • 链式调用 flatMap,遇到失败立即返回

这里以 Result<Value, Error> 为例:

func chain<ResultType: ResultConvertible>(
    _ initial: ResultType,
    operations: [(ResultType.Value) -> ResultType]
) -> ResultType {
    return operations.reduce(initial) { partialResult, op in
        partialResult.flatMap(op)
    }
}

3️⃣ 定义 ResultConvertible 协议

为了让 chain 泛型化支持 Result-like Monad:

protocol ResultConvertible {
    associatedtype Value
    associatedtype Failure: Error
    
    func flatMap(_ transform: (Value) -> Self) -> Self
}

// 让 Swift 原生 Result 符合
extension Result: ResultConvertible {
    func flatMap(_ transform: (Success) -> Result<Success, Failure>) -> Result<Success, Failure> {
        switch self {
        case .success(let value):
            return transform(value)
        case .failure(let error):
            return .failure(error)
        }
    }
}

4️⃣ 使用示例

定义几个操作函数:

enum AppError: Error {
    case negative
    case tooLarge
}

func addTen(_ x: Int) -> Result<Int, AppError> {
    x + 10 >= 0 ? .success(x + 10) : .failure(.negative)
}

func double(_ x: Int) -> Result<Int, AppError> {
    x * 2 <= 100 ? .success(x * 2) : .failure(.tooLarge)
}

使用 chain 函数:

let initial: Result<Int, AppError> = .success(5)

let operations: [(Int) -> Result<Int, AppError>] = [addTen, double]

let finalResult = chain(initial, operations: operations)

switch finalResult {
case .success(let value):
    print("Final value:", value) // 30
case .failure(let error):
    print("Error:", error)
}
  • 5 → addTen → 15 → double → 30 ✅
  • 如果输入是 -20 → addTen 返回失败 → 后续 double 不执行

5️⃣ 可拓展性

  1. Optional 也可以通过协议适配
extension Optional {
    func flatMap<B>(_ transform: (Wrapped) -> Optional<B>) -> Optional<B> {
        switch self {
        case .some(let value): return transform(value)
        case .none: return nil
        }
    }
}

然后可以写一个类似 chain 的泛型函数,处理 [Optional<Int>] 流水线。

  1. Publisher 也可泛型
  • Publisher 的 flatMap 已经符合 Monad 行为
  • 可以用类似 reduce 的方式组合多个 Publisher 操作

6️⃣ 核心设计思想

  • 核心操作函数返回 同类型 Monad → 保持 Monad bind 语义
  • flatMap 用于组合操作 → 避免嵌套
  • reduce 或链式调用 → 保证类型安全
  • 错误自动传播 → 不需要 try/catch