関数型プログラミング

純粋関数 じゅんすいかんすう

関数型プログラミング副作用参照透過性イミュータブルテスト容易性冪等性
純粋関数について教えて

簡単に言うとこんな感じ!

同じ材料を入れたら必ず同じ料理が出てくる、しかも調理中にキッチンを汚さない、そんなお行儀のいい関数のことだよ!「同じ入力 → 必ず同じ出力」「外の世界に影響を与えない」この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年代JavaScriptPythonが関数型スタイルを取り込み始める。map filter reduce などの純粋な高階関数が普及
  • 2010年代React.js(2013年)がUIコンポーネントを「同じprops→同じ表示」という純粋関数的思想で設計し、フロントエンド開発に純粋関数の概念が広まる
  • 現在RustScalaKotlinTypeScriptなど主要言語が関数型スタイルを積極採用。純粋関数の重要性はさらに高まっている

純粋関数 vs 不純な関数の比較・全体像

副作用がどこで発生するかを図示します。純粋関数は「閉じた世界」で完結し、不純な関数は外部状態と相互作用します。

✅ 純粋関数 ❌ 不純な関数 引数(Input) 例: add(2, 3) 関数本体 外部に触れない 戻り値(Output) 例: 5(常に同じ) 外部状態:一切変化なし 🔒 テスト容易・予測可能 引数(Input) 例: getUser(id) 関数本体 外部DBを参照・書込 戻り値(Output) 毎回変わる可能性 外部状態(DB・グローバル変数) ← 読み書きが発生する ⚠️

関数型プログラミング言語との関係

言語純粋関数の扱い特徴
Haskellデフォルトで純粋副作用はモナドで明示管理
Elmアーキテクチャ全体が純粋フロントエンド向け
Scala / F#純粋スタイルを推奨実用的なハイブリッド
JavaScript混在(書き方次第)Array.map 等は純粋
Python混在(書き方次第)map filter は純粋

関連用語