Effect 的 catchTag 怎么精确捕获错误?

处理错误最糟的结果是:你给 getUser 新增了一种失败,调用方却没有任何提示,新错误悄悄漏了过去。

用 Effect.catchTag("标签", handler):它只捕获带这个 _tag 的 failure,处理完之后,类型里的 E 会真的不再有它。 这样错误清单由编译器维护,而不是靠文档和记忆。

这里默认你已经有了带 _tag 的错误类(写法见 Data.TaggedError 怎么用),并且知道 Effect<A, E, R> 里的 E 描述可能的失败。下面看单个捕获、批量捕获,以及什么时候不该用兜底的 Effect.catch。

catchTag 怎么让类型收窄?

假设 getUser 的 E 是 NotFound | Unauthorized。组合多个 Effect 时,联合会自动累积;处理时按标签捕获其中一种:

const program = getUser("42").pipe(
  Effect.catchTag("NotFound", (e) => Effect.succeed(guestUser(e.id)))
)
// Effect<User, Unauthorized>  —— NotFound 已从 E 中消失

如果之后给 getUser 新增了一种失败 RateLimited,所有调用点的 E 会立刻多出这一项,编译器会逼着你决定怎么办。

多种错误怎么一次处理?

用 Effect.catchTags,给每个标签一个处理函数:

const handled = getUser("42").pipe(
  Effect.catchTags({
    NotFound: (e) => Effect.succeed(guestUser(e.id)),
    Unauthorized: () => Effect.succeed(anonymous)
  })
)
// Effect<User, never>

能不能一把兜住所有 failure?

可以,用 Effect.catch。但它会把你将来新增的 failure 也一并吞掉,类型清单的好处就被浪费了。需要分类型处理时,优先用 catchTag 或 catchTags。

catchTag 接得住 defect 吗?

接不住。catch、catchTag 只处理 failure,不碰 defect,这是设计而不是缺陷,原因和处理位置见 defect 要在哪里处理。更进一步的捕获方式可以看 catchReason 和 catchFilter,错误设计的整体思路见 错误处理的分层设计。

本文内容整理自本站 Effect 教程中错误一章,原章节没有标明所用的 effect 版本,也没有记录示例的运行结果;这里的代码我没有重新运行验证,API 以你所用版本的 Effect 官方文档为准。