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