心智模型与准备
L04 pipe、Effect.gen 与组合的本质
本课目标
- 熟练写出 pipe 链和 gen 两种风格
- 理解 yield* 为什么能自动解包 Success
前置课时尚未完成:L03
可以自行跳转学习,但按顺序推进会更省力;学情分析会把这类缺口归入「前置缺口」。
两种写法,同一棵树
pipe 是函数式流水线:每个算子接收一个 Effect、返回一个新 Effect。Effect.gen 用生成器把同样的嵌套结构写成看起来像同步代码的形态。二者产出的都是不可变蓝图,没有任何执行发生。
等价的两种表达
← 代码可左右滑动 →
import { Effect } from "effect"
const pipeline = Effect.succeed(1).pipe(
Effect.map((n) => n + 1),
Effect.tap((n) => Effect.log("value: " + n)),
Effect.flatMap((n) => Effect.succeed(n * 10))
)
const generator = Effect.gen(function* () {
const a = yield* Effect.succeed(1)
const b = a + 1
yield* Effect.log("value: " + b)
return yield* Effect.succeed(b * 10)
})
await Effect.runPromise(generator) // => 20💡 每个 Effect 都实现 Symbol.iterator,yield* 委托给一个单次生成器,由 runtime 在解释时恢复执行。
🔬 yield* 不是 await
yield* 拿到的是 Success 值;失败会直接短路整个生成器,并把该错误并入外层 Effect 的 E 通道。同时每个被 yield* 的 Effect 的 R 也会并入外层的 Requirements——类型就是这么「长出来」的。
交互演练
关键术语
组合子 (combinator)
接收 Effect 并返回新 Effect 的算子
SingleShotGen
配合 Symbol.iterator 让 Effect 可被 yield* 的内部迭代器
常见误区
× 在 Effect.gen 里用普通 await,绕过类型系统与中断机制。
× 把 yield* 写成 yield(少了星号),拿到的是 Effect 本身而不是结果。
随堂测验(即时反馈,答题计入掌握度)
Q1Effect.gen 里 yield* 某个失败 Effect 会怎样?
Q2pipe 链中每个算子的返回值是?