Effect 深度课effect@4.1.4 · LTS
核心值与组合

L07 类型是怎么长出来的:E 与 R 的累积

⏱ 30 min难度 3/5前置:L06
本课目标
  • 能手工推导一段 gen 程序的最终类型
  • 理解为什么「编译通过即依赖齐全」
前置课时尚未完成:L06

可以自行跳转学习,但按顺序推进会更省力;学情分析会把这类缺口归入「前置缺口」。

编译器替你做依赖记账

Effect.gen 里每 yield* 一个 Effect,它的 Error 会并入外层 E 的联合类型,Requirements 会并入外层 R。反过来,Effect.catch 处理掉某个错误后它会从 E 中消失,Effect.provide 提供某个服务后它会从 R 中消失。

一次完整的类型变化
← 代码可左右滑动 →
import { Context, Effect } from "effect"

class Db extends Context.Service<Db, {
  readonly find: (id: number) => Effect.Effect<string, NotFound>
}>()("Db") {}

// yield* Db            -> R 增加 Db
// yield* db.find(id)   -> E 增加 NotFound
const program = Effect.gen(function* () {
  const db = yield* Db
  return yield* db.find(1)
})
// Effect<string, NotFound, Db>

const handled = program.pipe(
  Effect.catchTag("NotFound", () => Effect.succeed("fallback")),
  Effect.provide(DbLive)
)
// Effect<string, never, never>  -> 现在可以直接 runPromise
💡 类型层面的「减法」是 Effect 最有生产力的部分:未处理的错误与未提供的依赖都过不了编译。
🔬 架构编码进类型

一个用例函数只要签名里还留着 R,就说明它还有依赖没被满足。于是「六边形架构」「端口与适配器」不再是目录约定,而是可检查的类型事实:Tag/Service 是端口,Layer 是适配器。

交互演练
关键术语
联合类型累积
多个 yield* 的 E 汇成 union,R 汇成服务集合
catchTag
按 _tag 精确恢复某类错误,并把它从 E 中消除
常见误区
× 在库代码里提前 provide 掉所有依赖,让上层失去替换实现的能力。
随堂测验(即时反馈,答题计入掌握度)
Q1Effect.provide 对类型的影响是?
Q2R = never 意味着什么?