Skip to content

デュアル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を使用する際に使われるバリアントです。最初の引数としてEffectpipe関数に渡し、その後に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に対して単一の操作を実行するだけの時に便利です。