純粋関数
同じ入力に対して常に同じ出力を返し、副作用を持たない関数
関数型プログラミング設計
純粋関数とは
純粋関数 (Pure Function) は、(1) 同じ入力に対して常に同じ出力を返し、(2) 副作用を持たない関数である。テストが容易で、並行処理でも安全に使える。
純粋 vs 不純
// ✅ 純粋関数
function add(a: number, b: number): number {
return a + b; // 入力のみに依存、外部状態を変更しない
}
function formatPrice(amount: number): string {
return `¥${amount.toLocaleString()}`;
}
// ❌ 不純な関数
let total = 0;
function addToTotal(amount: number): number {
total += amount; // 外部変数を変更 (副作用)
return total;
}
function getCurrentTime(): string {
return new Date().toISOString(); // 呼び出すたびに結果が変わる
}
純粋関数の条件
純粋関数は 3 つの条件を満たす。同じ入力に対して常に同じ出力を返す参照透過性、外部状態を変更しない副作用の排除、グローバル変数・DB・時刻などの外部状態に依存しないことだ。
テストの容易さ
// ✅ 純粋関数: 入力と出力だけテストすればよい
test('add', () => {
expect(add(1, 2)).toBe(3);
expect(add(-1, 1)).toBe(0);
});
// ❌ 不純な関数: モック、セットアップ、クリーンアップが必要
test('addToTotal', () => {
total = 0; // グローバル状態をリセット
expect(addToTotal(10)).toBe(10);
expect(addToTotal(20)).toBe(30);
total = 0; // クリーンアップ
});
実践的なパターン
// ❌ ビジネスロジックと副作用が混在
async function processOrder(orderId: string) {
const order = await db.get(orderId); // 副作用
const total = order.items.reduce((s, i) => s + i.price, 0);
const tax = total * 0.1;
await db.update(orderId, { total, tax }); // 副作用
}
// ✅ 純粋なロジックを分離
function calculateOrder(items: Item[]): { total: number; tax: number } {
const total = items.reduce((s, i) => s + i.price, 0);
return { total, tax: total * 0.1 };
}
async function processOrder(orderId: string) {
const order = await db.get(orderId);
const result = calculateOrder(order.items); // 純粋関数
await db.update(orderId, result);
}
React と純粋関数
React のコンポーネントは純粋関数であるべき。同じ props に対して常に同じ JSX を返す。副作用は useEffect に隔離する。
配列操作の純粋性
// ❌ 破壊的メソッド (元の配列を変更)
arr.sort(); // 元の配列をソート
arr.push(1); // 元の配列に追加
// ✅ 非破壊的メソッド (新しい配列を返す)
arr.toSorted(); // 新しいソート済み配列
[...arr, 1]; // 新しい配列
arr.filter(x => x > 0); // 新しい配列
実践的な知識は関連書籍でも得られる。