
JavaScriptで理解するcomposeとpipe:コードを簡潔にする関数合成の基本
JavaScriptでよく使用される「compose(コンポーズ)」と「pipe(パイプ)」の概念について解説します。JavaScriptの経験がある方であれば、これらの概念に出会ったことがあるかもしれません。これらの概念は比較的シンプルですが、時として混乱を招くことがあります。それでは、より良い理解のために詳しく見ていきましょう。
まず、これらの類似点について見てみましょう。composeとpipeは、どちらもデータ変換と関数合成において重要な概念です。簡単に言えば、機能を小さな再利用可能なコンポーネントに分割することで複雑さを軽減します。これらの小さな関数を組み合わせることで、より複雑な機能を作成でき、コードの再利用性と可読性が向上します。
composeとpipeは、どちらも複数の関数を引数として受け取り、それらの関数を組み合わせた新しい関数を返します。
compose(コンポーズ)
composeは複数の関数を引数として受け取り、それらの関数の合成である新しい関数を返します。重要なポイントは、関数の実行順序が右から左であることです。つまり、最も右の関数が最初に実行され、その出力が左隣の関数の引数として渡され、以降同様に処理が続きます。
例
const add2 = (x) => x + 2;
const square = (x) => x * x;
const subtract2 = (x) => x - 2;
const compose = (...functions) => (value) =>
functions.reduceRight((acc, fn) => fn(acc), value);
const composedResult = compose(subtract2, square, add2);
console.log(composedResult(4));
説明
まず、4がadd2に渡され、結果は6となります。
次に、6がsquareで二乗され、結果は36となります。
最後に、subtract2で2が引かれ、最終的な出力は34となります。

pipe(パイプ)
pipeはcomposeと似ていますが、関数の実行順序が左から右になります。これにより、データの流れが関数の順序と一致するため、一部の開発者にとってより直感的です。
例
const add2 = (x) => x + 2;
const square = (x) => x * x;
const subtract2 = (x) => x - 2;
const pipe = (...functions) => (value) =>
functions.reduce((acc, fn) => fn(acc), value);
const pipedResult = pipe(subtract2, square, add2);
console.log(pipedResult(4));
説明
まず、4がsubtract2に渡され、結果は2となります。
次に、2がsquareで二乗され、結果は4となります。
最後に、add2で2が加えられ、最終的な出力は6となります。

composeとpipeを使用した複雑さの軽減
以下のような処理が必要なシステムを想像してみてください:
ユーザー入力の検証
入力のフォーマット
データベースへの保存
composeやpipeを使用しない場合、これは入れ子になった関数呼び出しのように見えるでしょう。
storeInDB(formatInput(validateInput(userInput)));
composeを使用して改善する例
const validateInput = (input) => {
if (!input || input.trim() === '') {
throw new Error('Invalid input');
}
return input.trim();
};
const formatInput = (input) => input.toLowerCase();
const storeInDB = (input) => {
console.log(`Storing "${input}" in the database.`);
return `Stored: ${input}`;
};
// Compose the steps
const compose = (...functions) => (value) =>
functions.reduceRight((acc, fn) => fn(acc), value);
const processUserInput = compose(storeInDB, formatInput, validateInput);
console.log(processUserInput('my input'));
同じ処理をpipeで実装する例
const pipe = (...functions) => (value) =>
functions.reduce((acc, fn) => fn(acc), value);
const processUserInputWithPipe = pipe(validateInput, formatInput, storeInDB);
console.log(processUserInputWithPipe('my input'));
もう一つの例として、以下の操作を計算する必要がある場合を見てみましょう。
10%の税金を追加
割引の適用
価格を通貨形式にフォーマット
composeとpipeを使用しない場合
const tax = (amount) => amount + amount * 0.1;
const discount = (amount) => amount - 10;
const formatCurrency = (amount) => `$${amount.toFixed(2)}`;
const finalPrice = formatCurrency(discount(tax(100)));
console.log(finalPrice);
composeを使用した場合
const compose = (...functions) => (value) =>
functions.reduceRight((acc, fn) => fn(acc), value);
const calculateTotalWithCompose = compose(formatCurrency, discount, tax);
console.log(calculateTotalWithCompose(100));
pipeを使用した場合
const pipe = (...functions) => (value) =>
functions.reduce((acc, fn) => fn(acc), value);
const calculateTotalWithPipe = pipe(tax, discount, formatCurrency);
console.log(calculateTotalWithPipe(100));
上記の例から、composeとpipeを使用することで、コードの複雑さを軽減し、可読性を向上させることができることが分かります。
composeとpipeの選択について
どちらを選択するかは、関数の実行順序をどのように考えるかによって決まります。目的の結果を得るために、必ずしもcomposeとpipeを切り替える必要はありません。関数に渡す引数の順序を変更するだけで、どちらの方法でも同じ結果を得ることができます。
composeとpipeは、関数型プログラミングにおける強力なツールであり、複雑な機能を実現しながら、簡潔で再利用可能なコードを書くことを可能にします。次回、複雑な関数を書く必要が出てきた際は、ここで学んだ概念の適用を検討してみてください。それでは、楽しいコーディングを!
この記事は、2025 年 1 月に弊社のエンジニア Surendra Sedai が執筆し、日本語に翻訳したものです。
英語版はこちらをクリックしてください。
https://articles.wesionary.team/compose-and-pipe-in-javascript-a19102d36394
採用情報
私たちはプロダクト共創の仕組み化に取り組んでいます。プロダクト共創をリードするプロダクト・マネージャー、そして、私たちのビジョンを市場に届ける営業メンバーを募集しています!
開発パートナーをお探しの企業様へ
弊社は、グローバル開発のメリットを活かし、高い費用対効果と品質を両立しています。経験豊富で多様性のあるチームが、課題を正しく理解し、最適なシステムと優れた体験を実現します。業務システムの開発、新規事業の開発、業務効率化やDX化に関するお困りごと、ぜひ弊社にご相談ください。