純粋関数 じゅんすいかんすう
関数型プログラミング副作用参照透過性イミュータブルテスト容易性冪等性
純粋関数について教えて
簡単に言うとこんな感じ!
同じ材料を入れたら必ず同じ料理が出てくる、しかも調理中にキッチンを汚さない、そんなお行儀のいい関数のことだよ!「同じ入力 → 必ず同じ出力」「外の世界に影響を与えない」この2つを守る関数を純粋関数って呼ぶんだ。
純粋関数とは
純粋関数(Pure Function)とは、関数型プログラミングにおける関数の理想的なあり方を指します。純粋関数には2つの絶対条件があります。①同じ引数を渡せば必ず同じ値を返す(参照透過性)、②関数の実行が外部の状態に影響を与えない(副作用がない)、この2点を満たす関数だけが「純粋」と呼ばれます。
逆に、グローバル変数を書き換えたり、ファイルやデータベースへ書き込んだり、画面に表示したり、乱数を返したりする関数は副作用(Side Effect)を持つため、純粋関数ではありません。「同じ引数なのに結果が変わる可能性がある」「こっそり外の状態を変える」関数は不純(Impure)と呼ばれます。
ビジネスパーソン的に言うと、純粋関数は「仕様書どおりにしか動かない、影響範囲が完全に読める部品」です。テストしやすく、バグを追いやすく、再利用しやすい。ソフトウェアの品質を高める上で、純粋関数を増やすことは非常に効果的な設計方針です。
純粋関数の2大ルール
| ルール | 意味 | 違反する例 |
|---|---|---|
| 参照透過性(Referential Transparency) | 同じ入力 → 常に同じ出力 | 現在時刻・乱数・外部DBの値を返す |
| 副作用なし(No Side Effects) | 関数の外の状態を変えない | グローバル変数の書き換え・ファイル書き込み・ログ出力 |
✅ 純粋関数の例 vs ❌ 不純な関数の例
✅ 純粋関数
function add(a, b) {
return a + b; // 同じ a,b なら必ず同じ結果
}
✅ 純粋関数
function double(arr) {
return arr.map(x => x * 2); // 元の配列を変えない
}
❌ 不純な関数(副作用あり)
let count = 0;
function increment() {
count++; // 外のグローバル変数を変えている
return count;
}
❌ 不純な関数(参照透過でない)
function getRandom() {
return Math.random(); // 同じ呼び出しでも毎回結果が違う
}
覚え方:「純粋関数はブラックボックスの優等生」
入れたものが出てくるだけ、何も持ち込まず何も持ち出さない。
空港の手荷物検査を通るように、余計なものは一切やり取りしない関数、と覚えましょう。
純粋関数のメリット
| メリット | 理由 | 実務上の効果 |
|---|---|---|
| テストが簡単 | 入力と出力だけ確認すればよい | テストコードが短く、環境依存がない |
| バグ追跡が楽 | 影響範囲が関数内に閉じている | 「どこで状態が変わったか」を追わなくていい |
| 並列処理に強い | 状態を共有しないので競合が起きない | マルチスレッド・分散処理での安全性が上がる |
| 再利用しやすい | 依存関係がない | 別プロジェクトでもそのまま使える |
| 最適化しやすい | 同じ入力なら結果をキャッシュできる(メモ化) | 処理速度の向上が図れる |
歴史と背景
- 1930年代 — 数学者アロンゾ・チャーチがラムダ計算(λ計算)を提唱。関数を数学的に厳密に定義したのが純粋関数の源流
- 1950年代 — LISP言語が登場し、関数を「第一級市民(値として扱えるもの)」として実装。純粋関数の概念がプログラミングへ
- 1987年 — Haskellの設計が始まり、純粋関数型言語として1990年にリリース。副作用はすべて「モナド」という仕組みで管理
- 2000年代 — JavaScriptやPythonが関数型スタイルを取り込み始める。
mapfilterreduceなどの純粋な高階関数が普及 - 2010年代 — React.js(2013年)がUIコンポーネントを「同じprops→同じ表示」という純粋関数的思想で設計し、フロントエンド開発に純粋関数の概念が広まる
- 現在 — RustやScala、Kotlin、TypeScriptなど主要言語が関数型スタイルを積極採用。純粋関数の重要性はさらに高まっている
純粋関数 vs 不純な関数の比較・全体像
副作用がどこで発生するかを図示します。純粋関数は「閉じた世界」で完結し、不純な関数は外部状態と相互作用します。
関数型プログラミング言語との関係
| 言語 | 純粋関数の扱い | 特徴 |
|---|---|---|
| Haskell | デフォルトで純粋 | 副作用はモナドで明示管理 |
| Elm | アーキテクチャ全体が純粋 | フロントエンド向け |
| Scala / F# | 純粋スタイルを推奨 | 実用的なハイブリッド |
| JavaScript | 混在(書き方次第) | Array.map 等は純粋 |
| Python | 混在(書き方次第) | map filter は純粋 |
関連用語
- 001-functional-programming.md — 関数を中心に処理を組み立てるプログラミングパラダイム
- 054-side-effect.md — 関数が外部の状態を変える「副作用」とその扱い方
- 055-referential-transparency.md — 同じ式が常に同じ値に置き換え可能であるという性質
- 056-immutable.md — 作成後に値を変更できない「イミュータブル」なデータ
- 057-higher-order-function.md — 関数を引数や戻り値として扱う高階関数
- 058-memoization.md — 同じ入力の結果をキャッシュして高速化するメモ化(純粋関数があるから使える)
- 059-monad.md — Haskellなどで副作用を安全に扱うための設計パターン
- 060-lambda.md — 名前のない無名関数(ラムダ式)と純粋関数の関係