関数型プログラミング

不変データ構造 ふへんでーたこうぞう

イミュータブル関数型プログラミング副作用状態管理永続データ構造React
不変データ構造について教えて

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

一度作ったら二度と書き換えられないデータのことだよ!「変更したい」ときは元のデータはそのままにして、変更内容を反映した新しいデータをコピーして作る仕組みなんだ。消せない付箋みたいなイメージで、改ざんされる心配がなくてバグも追いやすいってこと!


不変データ構造とは

不変データ構造(Immutable Data Structure/イミュータブル・データ・ストラクチャ)とは、一度生成したら中身を変更できないデータのまとまりのことです。プログラムの中でデータを「書き換える」のではなく、「変更を加えた新しいコピーを作る」という考え方に基づいています。

対義語は可変データ構造(Mutable)で、普段のプログラミングでは配列や辞書型オブジェクトの中身を好きなタイミングで書き換えるのが一般的です。しかし不変データ構造では、たとえばリストに要素を追加したいときも、「元のリストはそのまま残して、要素が追加された新しいリストを返す」という操作になります。

この仕組みは関数型プログラミングの根幹をなす考え方で、データが勝手に変わることで起きるバグ(副作用)を根本から防げるため、近年のフロントエンド開発やバックエンド設計でも広く採用されています。


不変データ構造の仕組み

可変 vs 不変:何が違う?

比較項目可変(Mutable)不変(Immutable)
データの変更元のデータを直接書き換える新しいコピーを作って返す
元データ消える・変わる常に残る
バグの発生意図しない変更が起きやすい変更が追跡しやすい
メモリ効率一つのデータを再利用単純コピーだと多くなる※
並列処理競合・ロックが必要安全(競合しない)

※ 後述の「構造的共有」で効率化される

イメージで理解する

【可変データ構造】
リスト = [1, 2, 3]
リスト.add(4)
→ リスト = [1, 2, 3, 4]  ← 元のリストが破壊される!

【不変データ構造】
リスト1 = [1, 2, 3]
リスト2 = リスト1.add(4)
→ リスト1 = [1, 2, 3]   ← 元データはそのまま残る
→ リスト2 = [1, 2, 3, 4] ← 新しいデータができる

語呂合わせで覚える

「イミュータブル=イジれないブル(強い牛)」 頑丈で誰にも変えられない、強い牛のようなデータ!

構造的共有(Structural Sharing)でメモリを節約

単純にコピーを作るだけだとメモリが何倍にも増えてしまいます。そこで多くの不変データ構造ライブラリは構造的共有という技術を使っています。変更のなかった部分は元のデータへの参照を使い回すことで、コピーのコストを最小限に抑えます。

元データ:  [A] → [B] → [C] → [D]

"C"を変更した新データ:
           [A] → [B] → [C'] → [D]
                         ↑新しく作る
           [A][B][D] は元データと共有(コピーしない)

歴史と背景

  • 1950〜60年代 — LISP言語が登場。リストを不変として扱う関数型プログラミングの原型が生まれる
  • 1970〜80年代 — MLやHaskellなど関数型言語が学術分野で発展。不変性が設計の中心に
  • 1990年代JavaやC++など手続き型言語が主流となり、可変データ構造が業界標準に
  • 2000年代 — 並列・並行処理の重要性が増し、可変データによる競合問題が深刻化。不変性が再評価される
  • 2013年Immutable.js(Facebook製)が登場。JavaScriptに不変データ構造を持ち込み注目を集める
  • 2015年頃ReduxReactの状態管理ライブラリ)が不変性を設計の中心に据えて普及。フロントエンド開発での採用が急増
  • 2020年代RustKotlinSwiftなどモダン言語でも不変性を言語レベルでサポートする設計が標準化

関連する技術・ライブラリとの比較

主要な不変データ構造ライブラリ

ライブラリ言語特徴
Immutable.jsJavaScriptFacebookが開発。List/Map/Setなどを提供
ImmerJavaScript普通の書き方でコードを書けて内部で不変にしてくれる
ClojureJVM言語言語全体が不変データ構造ベース
Haskell関数型言語すべてのデータがデフォルトで不変

不変性と状態管理の関係(SVG図解)

不変データ構造による状態管理フロー 現在の状態 State v1 アクション Action(変更要求) 新しい状態 State v2(新規作成) State v1 変更されず残る State v2 新しく生成される メリット ・変更前後を比較できる(タイムトラベル) ・v1は安全に参照できる(undo/redo可能)

実務でよく見る使われ方:Reduxとの組み合わせ

// ❌ 可変(NG):元のstateを直接書き換えてしまう
function badReducer(state, action) {
  state.count = state.count + 1; // 元データを破壊!
  return state;
}

// ✅ 不変(OK):スプレッド構文で新しいオブジェクトを返す
function goodReducer(state, action) {
  return { ...state, count: state.count + 1 }; // 新しいオブジェクトを作成
}

関連する規格・RFC

※ 不変データ構造はプログラミングの設計概念であり、IETFやISOの公式規格は存在しないため、このセクションは省略します。


関連用語

  • 関数型プログラミング — 副作用を排除し、関数の組み合わせでプログラムを構成するパラダイム
  • 副作用 — 関数の外部の状態を変更してしまう予期しない影響のこと
  • 純粋関数 — 同じ入力には必ず同じ出力を返し、副作用を持たない関数
  • 状態管理 — アプリケーション内のデータの変化を一元管理する仕組み
  • Redux — Reactアプリで広く使われる不変性ベースの状態管理ライブラリ
  • 並行処理 — 複数の処理を同時に行う際、不変性があると競合問題を防げる
  • スナップショット — ある時点のデータの状態を保存する概念。不変性と相性がよい