3-27.【函数式编程】讨论 Combine 的 Publisher 如何体现 Functor / Monad 特性。

12 阅读1分钟

1️⃣ Functor 特性在 Publisher 中的体现

Functor 核心:能够把函数映射到容器/上下文中的值,同时保持结构不变。

在 Combine 中:

  • Publisher 可以看作一个 容器,异步发出值
  • 使用 map 可以对 Publisher 发出的每个元素进行函数映射
  • 不改变 Publisher 的结构,只改变输出值

示例

import Combine
import Foundation

let publisher = [1, 2, 3].publisher // ArrayPublisher<Int>

let mapped = publisher.map { $0 * 2 }

let cancellable = mapped.sink { value in
    print("Mapped value:", value)
}
// 输出:Mapped value: 2
//       Mapped value: 4
//       Mapped value: 6
  • map 只改变 输出值,不改变 Publisher 的类型
  • 多次 map 可组合,满足 Functor 规律:
let f: (Int) -> Int = { $0 + 1 }
let g: (Int) -> Int = { $0 * 10 }

let left = publisher.map(f).map(g)
let right = publisher.map { g(f($0)) }
  • left == right ✅ 组合律满足

2️⃣ Monad 特性在 Publisher 中的体现

Monad 核心:支持 bind(flatMap)操作,能够把返回容器的函数平铺,避免嵌套容器。

在 Combine 中:

  • flatMap 对应 Monad 的 bind
  • 输入:(Output) -> Publisher<NewOutput, Failure>
  • 输出:单一 Publisher,把可能产生的多个嵌套 Publisher 平铺
  • 支持链式组合多个异步操作

示例

假设我们有两个异步 Publisher 函数:

func fetchNumber(_ id: Int) -> AnyPublisher<Int, Never> {
    Just(id * 2).eraseToAnyPublisher()
}

func fetchString(_ number: Int) -> AnyPublisher<String, Never> {
    Just("Number is (number)").eraseToAnyPublisher()
}

// 使用 flatMap 链式组合
let cancellable2 = [1, 2, 3].publisher
    .flatMap { fetchNumber($0) }   // Monad bind,平铺 Publisher
    .flatMap { fetchString($0) }
    .sink { print($0) }

解释

  1. [1,2,3].publisher → Publisher

  2. .flatMap(fetchNumber) → Publisher

    • 每个值会生成一个新的 Publisher
    • flatMap 平铺输出,而不是产生嵌套 Publisher<Publisher>
  3. .flatMap(fetchString) → Publisher

  4. 最终输出一个 扁平 Publisher,可以订阅


3️⃣ Functor vs Monad 对比在 Combine 中

特性Functor (map)Monad (flatMap)
操作类型(Output) -> NewOutput(Output) -> Publisher<NewOutput, Failure>
结构变化不改变 Publisher 层级平铺嵌套 Publisher
适用场景对值做同步转换异步链式调用,生成新的 Publisher
示例.map { $0 * 2 }.flatMap { fetchNumber($0) }

4️⃣ 总结

  1. Functor

    • Publisher.map → 映射输出值,保持 Publisher 结构
    • 遵循恒等和组合律
  2. Monad

    • Publisher.flatMap → 绑定新的 Publisher,平铺嵌套,短路失败可处理
    • 支持函数式异步流水线,链式组合

💡 核心思想

Combine 的 Publisher 就是一个 异步容器,Functor 和 Monad 的特性保证了:

  1. 可以安全映射数据 (map)
  2. 可以安全组合异步操作 (flatMap)
  3. 异步值处理保持函数式风格,副作用集中在 sinkassign