副作用

関数が戻り値以外に外部の状態を変更する操作で、テストやデバッグを困難にする要因

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

副作用とは

副作用 (Side Effect) は、関数が戻り値を返す以外に外部の状態を変更する操作である。DB への書き込み、ファイルの変更、API 呼び出し、グローバル変数の変更、コンソール出力が副作用に該当する。副作用のない関数を純粋関数 (Pure Function) と呼ぶ。

純粋関数 vs 副作用のある関数

// ✅ 純粋関数: 同じ入力に対して常に同じ出力、外部状態を変更しない
function add(a: number, b: number): number {
  return a + b;
}

function formatUser(user: User): string {
  return `${user.name} (${user.email})`;
}

// ❌ 副作用のある関数
let count = 0;
function increment(): number {
  count++;          // グローバル変数を変更 (副作用)
  return count;
}

async function saveUser(user: User): Promise<void> {
  await db.put(user);  // DB に書き込み (副作用)
  console.log('Saved'); // コンソール出力 (副作用)
}

副作用の種類

副作用
DB 操作 db.put(), db.delete()
API 呼び出し fetch(), axios.post()
ファイル操作 fs.writeFile()
コンソール出力 console.log()
グローバル変数の変更 window.location, モジュールレベル変数
DOM 操作 document.getElementById()
現在時刻の取得 Date.now(), new Date()

副作用を分離する

// ❌ ビジネスロジックと副作用が混在
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 });    // 副作用
  await sendEmail(order.userId, { total, tax }); // 副作用
}

// ✅ 純粋なロジックと副作用を分離
function calculateTotal(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 = calculateTotal(order.items); // 純粋関数を呼ぶ
  await db.update(orderId, result);
  await sendEmail(order.userId, result);
}

React の useEffect

React では副作用を useEffect に集約する。

function UserProfile({ userId }: { userId: string }) {
  const [user, setUser] = useState<User | null>(null);

  useEffect(() => {
    fetch(`/api/users/${userId}`).then(r => r.json()).then(setUser);
  }, [userId]); // 副作用を useEffect に隔離

  // レンダリングは純粋関数
  return user ? <div>{user.name}</div> : <div>Loading...</div>;
}

テストへの影響

関数の種類 テストの容易さ
純粋関数 容易 (入力と出力だけ検証)
副作用あり 困難 (モックが必要)

実務での活用方法は関連書籍にも詳しい。

関連用語