副作用
関数が戻り値以外に外部の状態を変更する操作で、テストやデバッグを困難にする要因
関数型プログラミング設計
副作用とは
副作用 (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>;
}
テストへの影響
| 関数の種類 | テストの容易さ |
|---|---|
| 純粋関数 | 容易 (入力と出力だけ検証) |
| 副作用あり | 困難 (モックが必要) |
実務での活用方法は関連書籍にも詳しい。