1️⃣ 定义 Monad 协议
// 定义一个泛型 Monad 协议
protocol Monad {
associatedtype Value
associatedtype ErrorType: Error
// bind / flatMap
func flatMap<B>(_ transform: (Value) -> Self) -> Self
}
Swift 的类型系统无法直接统一
Optional和Result,所以我们可以针对Result或Optional写泛型函数。
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️⃣ 可拓展性
- 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>] 流水线。
- Publisher 也可泛型:
- Publisher 的
flatMap已经符合 Monad 行为 - 可以用类似 reduce 的方式组合多个 Publisher 操作
6️⃣ 核心设计思想
- 核心操作函数返回 同类型 Monad → 保持 Monad bind 语义
flatMap用于组合操作 → 避免嵌套reduce或链式调用 → 保证类型安全- 错误自动传播 → 不需要 try/catch