デュアルAPI
Effect エコシステムで API を扱う際に、同じ API を異なる方法で使用する二つのスタイルが存在します。この二つのスタイルは「data-last」と「data-first」と呼ばれます。
技術的な観点から見ると、これらのバリアントは二つの TypeScript のオーバーロードを使用して実装されています。
API が両方のバリアントをサポートしている場合、それを「デュアル API」と呼びます。
具体的なデュアル API の例として、Effect.mapを使ってこれらの二つのバリアントを探ってみましょう。
Effect.map関数は二つの TypeScript のオーバーロードで定義されています。「data-last」と「data-first」という用語は、二つのオーバーロードのシグネチャにおけるself引数(データとも呼ばれる)の位置を示しています:
export 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引数が最後の位置にあります:
<A, B>(f: (a: A) => B): <E, R>(self: Effect<A, E, R>) => Effect<B, E, R>これはpipeを使用する際に使われるバリアントです。最初の引数としてEffectをpipe関数に渡し、その後にEffect.andThenを呼び出します:
const mappedEffect = pipe(effect, Effect.andThen(func));このバリアントは、長いパイプライン内で複数の計算をチェーンする必要がある場合に便利です。初回の変換の後にさらに計算を追加することで、パイプラインを続けることができます:
pipe(effect, Effect.andThen(func1), Effect.andThen(func2), ...)data-first
二つ目のオーバーロードでは、self引数が最初の位置にあります:
<A, E, R, B>(self: Effect<A, E, R>, f: (a: A) => B): Effect<B, E, R>このバリアントはpipe関数を必要としません。その代わり、Effect.andThen関数にEffectを最初の引数として直接渡すことができます:
const mappedEffect = Effect.andThen(effect, func);このバリアントは、Effectに対して単一の操作を実行するだけの時に便利です。