純粋関数

同じ入力に対して常に同じ出力を返し、副作用を持たない関数

関数型プログラミング設計

純粋関数とは

純粋関数 (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); // 新しい配列

実践的な知識は関連書籍でも得られる。

関連用語