已发布 上游基线 bf46254 原文 ↗ 在 GitHub 编辑

Dual API

探索 Effect 生态系统中 dual API 的 data-first 与 data-last 两种变体。

在 Effect 生态系统中使用 API 时,你可能会遇到同一个 API 有两种不同的用法。 这两种用法分别称为 “data-last” 变体和 “data-first” 变体。

当一个 API 同时支持这两种变体时,我们称它为 “dual” API。

下面用 Effect.map 来展示这两种变体。

Effect.map 作为 dual API

Effect.map 函数由两个 TypeScript 重载定义。“data-last” 和 “data-first” 这两个说法,指的是 self 参数(也称为 “data”)在两个重载签名中的位置:

declare const map: {
  //                               ┌─── data-last
  //                               ▼
  <A, B>(f: (a: A) => B): <E, R>(self: Effect<A, E, R>) => Effect<B, E, R>
  //             ┌─── data-first
  //             ▼
  <A, E, R, B>(self: Effect<A, E, R>, f: (a: A) => B): Effect<B, E, R>
}

data-last

在第一个重载中,self 参数位于最后

declare const map: <A, B>(
  f: (a: A) => B,
) => <E, R>(self: Effect<A, E, R>) => Effect<B, E, R>

这种写法通常与 pipe 函数配合使用。你先把 Effect 作为初始参数传给 pipe,然后再链式调用 Effect.map 之类的变换:

示例(使用 data-last 配合 pipe

const mappedEffect = pipe(effect, Effect.map(func))

当你需要串联多个变换时,这种风格很有帮助,能让代码以管道的形式更易读懂:

pipe(effect, Effect.map(func1), Effect.map(func2), ...)

data-first

在第二个重载中,self 参数位于最前

declare const map: <A, E, R, B>(
  self: Effect<A, E, R>,
  f: (a: A) => B,
) => Effect<B, E, R>

这种形式不需要 pipe。你可以直接把 Effect 作为第一个参数传入:

示例(不使用 pipe 的 data-first 写法)

const mappedEffect = Effect.map(effect, func)

当你只需要对 Effect 执行单个操作时,这种写法很合适。

Choosing Between Styles

两种重载的结果完全相同。请选择最适合你的编码风格、并且对团队可读性最好的那一种。