概要
関数は「処理をまとめたもの」では終わらない。
それは**“データ変換の流れを抽象化し、制御構造を分割可能にする設計単位”**である。
JavaScriptは、関数を第一級オブジェクトとして扱う言語であり、
その特性を最大限に活かすことでカリー化・関数合成による設計のモジュール化・柔軟化が可能になる。
本稿では、カリー化と関数合成の基本、設計上の価値、実用的な構造戦略を提示する。
1. カリー化(Currying)とは
function add(a) {
return function (b) {
return a + b;
};
}
const add5 = add(5);
console.log(add5(3)); // → 8
- ✅ 複数引数の関数を一引数ずつ分割して再適用できる関数に変換
- ✅ 一部の引数を先に固定できる(部分適用)
2. 汎用的なカリー化関数の定義
function curry(fn) {
return function curried(...args) {
return args.length >= fn.length
? fn.apply(this, args)
: (...next) => curried(...args, ...next);
};
}
function multiply(a, b, c) {
return a * b * c;
}
const curriedMultiply = curry(multiply);
console.log(curriedMultiply(2)(3)(4)); // → 24
- ✅ 関数の引数を段階的に提供可能な形に変換
- ✅ パイプライン構造やDI的な使い方にも応用可
3. 関数合成(Composition)とは
const compose = (f, g) => (x) => f(g(x));
const double = x => x * 2;
const square = x => x * x;
const doubleThenSquare = compose(square, double);
console.log(doubleThenSquare(3)); // → 36
- ✅
f(g(x))
のように関数を外側から順に適用 - ✅ 入力 → 加工 → 出力 の直列化された処理設計に有効
4. 関数パイプラインの構造化(pipe)
const pipe = (...fns) => (x) => fns.reduce((v, f) => f(v), x);
const process = pipe(
x => x + 1,
x => x * 2,
x => `Result: ${x}`
);
console.log(process(3)); // → "Result: 8"
- ✅ 左から右へ読みやすい順序で適用
- ✅ データフローの明確化・ステップごとの分離に優れる
5. カリー化 × 合成による抽象的ビジネスロジックの設計例
const filterBy = curry((key, value, arr) =>
arr.filter(obj => obj[key] === value)
);
const users = [
{ id: 1, role: 'admin' },
{ id: 2, role: 'user' },
{ id: 3, role: 'admin' }
];
const filterAdmins = filterBy('role')('admin');
console.log(filterAdmins(users)); // → [{id:1, ...}, {id:3, ...}]
- ✅ フィルター条件を部分適用して再利用可能に
- ✅ 構造を関数に分解し、再構成できる設計に昇華
設計判断フロー
① 再利用性のある処理は、部分適用できるように構造化されているか?
② 関数を組み合わせるとき、合成によって処理の流れを明確化できるか?
③ 入力 → 加工 → 出力の構造をパイプラインとしてモデル化できているか?
④ 固定引数を渡すために、使い回し可能な中間関数に分解できているか?
⑤ ロジックの直列化によって副作用を制御できているか?
よくあるミスと対策
❌ 関数を定義したが、使い回しや再構成が困難
→ ✅ カリー化して引数を固定しやすくする
❌ ロジックが長くなり、読みづらい処理列に
→ ✅ 関数合成や pipe を使ってデータ変換の流れを明示化
❌ 引数の受け渡しが複雑でスパゲッティ構造に
→ ✅ 小さな関数に分解して合成 / 部分適用で構成可能にする
結語
カリー化と関数合成とは、
単なる構文のトリックではない。
それは**“ロジックを分解し、組み立て直し、制御可能な単位として再構成するための抽象化戦略”**である。
- 関数は「処理」ではなく「変換構造」として設計する
- カリー化は再利用性、合成は構造の明快化を担う
- 複雑なロジックは、関数として分割・再構成することで安全に制御可能
JavaScriptにおける関数抽象化とは、
“再利用可能な構造単位としてロジックを設計するアーキテクチャ戦略”である。