第4章のオブジェクト指向は「状態を持つオブジェクト」を中心に据える世界でした。本章では、それとは対極にある もう一つの大きな系譜——状態と副作用を避け、関数の合成で計算を組み立てる 関数型プログラミングを辿ります。そしてもう一つ、言語の安全性を静かに支えてきた縁の下の力持ち、 型システムの進化を見ていきます。この2つは、現代の言語デザインの最前線で再び主役になっています。
理論の源流 — λ計算(1930年代)
関数型の物語は、コンピュータが存在する前から始まっています。1930年代、数学者アロンゾ・チャーチが λ計算(lambda calculus)という計算の数学的モデルを考案しました。これは「すべての計算を、 関数の定義と適用だけで表現する」という驚くほどミニマルな体系です。
興味深いことに、チャーチのλ計算と、アラン・チューリングのチューリング機械は、同じ計算能力を持つことが証明されました (チャーチ=チューリングのテーゼ)。つまり「関数の適用」だけで、コンピュータにできるあらゆる計算が表現できる—— これが関数型プログラミングの理論的支柱です。そしてこの抽象的な数学を、最初に動く言語にしたのが、 第2章で登場したLISP(1958)でした。
関数型の核心 — 純粋さと参照透過性
関数型プログラミングの中心思想は「純粋関数(pure function)」です。純粋関数とは、 同じ入力には必ず同じ出力を返し、外部の状態を変更する「副作用」を持たない関数のこと。 この性質を参照透過性(referential transparency)と呼びます。
-- 純粋関数: 同じ入力には必ず同じ出力。副作用なし
double :: Int -> Int
double x = x * 2
-- 関数の合成で計算を組み立てる
-- map で各要素に関数を適用(命令的なループを書かない)
result :: [Int]
result = map double [1, 2, 3] -- => [2, 4, 6] なぜこれが重要なのでしょうか。純粋関数は外部に影響を与えず、外部からも影響されないため、 並列化が安全(順序を気にせず同時実行できる)、結果のキャッシュが可能(同じ入力なら再計算不要)、 そして形式的検証やテストが容易になります。OOPが「可変状態の管理」で悩んだまさにその部分を、 関数型は「そもそも状態を持たない」ことで回避するのです。
| 観点 | OOP/命令型 | 関数型 |
|---|---|---|
| 中心概念 | 状態を持つオブジェクト、逐次的な命令 | 純粋関数とその合成、式の評価 |
| 状態の扱い | 可変(mutable)を前提に管理 | 不変(immutable)を基本とする |
| ループ | for/while で明示的に反復 | map/filter/reduce や再帰で表現 |
| 得意なこと | 現実のモデル化、UIや状態機械 | 並列処理、データ変換、検証可能性 |
ML と型推論 — 「正しい型なら間違えない」
関数型のもう一つの大きな貢献が型システムです。1970年代前半、エディンバラ大学のロビン・ミルナーは、 定理証明システムLCFのメタ言語として ML を開発しました。MLが持ち込んだ革命が型推論(type inference)です。
それまで、静的な型を持つ言語(Pascalなど)では、変数や関数の型を人間がいちいち書く必要がありました。 MLは「型を書かなくても、コンパイラが文脈から型を自動的に推論する」仕組みを実現します。 ミルナーは1978年の論文「A Theory of Type Polymorphism in Programming」で型推論アルゴリズムWを提示し、 有名な標語「well-typed programs cannot go wrong(型が正しいプログラムは実行時に型エラーを起こさない)」を 数学的に証明しました。この仕組みは発明者2人の名からHindley-Milner型システムと呼ばれます。
-- 型注釈を書かなくても、コンパイラが型を推論する
square x = x * x -- コンパイラは square :: Num a => a -> a と推論
-- 多相型: どんな型のリストにも使える length
-- length :: [a] -> Int (aは任意の型)
len = length [1,2,3] -- => 3 Haskell(1990)— 純粋関数型の集大成
関数型の理想を最も純粋な形で結晶させたのが Haskell(1990年に最初の仕様)です。 Haskellは委員会によって設計された、研究と実用を橋渡しする標準的な純粋関数型言語で、3つの特徴を備えます。
- 純粋性の徹底: 副作用を型システムで隔離する。I/Oのような副作用は
IO型で明示的に扱う - 遅延評価(lazy evaluation): 値は本当に必要になるまで計算しない。無限リストすら自然に扱える
- 強力な型システム: 型クラス、モナドといった抽象化で、純粋さと実用性を両立する
-- 遅延評価の威力: 無限リストから先頭5個だけ取り出す
naturals :: [Integer]
naturals = [1..] -- 1から始まる無限リスト(だが即座には計算しない)
firstFive :: [Integer]
firstFive = take 5 naturals -- => [1,2,3,4,5] 必要な分だけ計算される Haskellは主流の座を獲得したわけではありませんが、その影響は絶大です。Haskellで磨かれた型クラス、 パターンマッチ、不変データ、モナドといったアイデアは、Scala・Rust・Swift・Kotlin、さらにはJavaやC#、JavaScriptへと流れ込み、 「関数型の良いところ」を主流言語が次々と取り込む流れを生みました。
静的 vs 動的 — 型をめぐる長い対立
型システムには、根深い対立軸があります。静的型付け(コンパイル時に型を決める)と 動的型付け(実行時に型が決まる)のどちらを選ぶか、です。
| 静的型付け | 動的型付け | |
|---|---|---|
| 代表 | C, Java, Haskell, Rust, TypeScript | Python, Ruby, JavaScript, LISP |
| 型チェック | コンパイル時(実行前) | 実行時 |
| 利点 | バグの早期発見、補完・最適化が効く | 柔軟で記述が軽快、試行錯誤が速い |
| 欠点 | 記述がやや増える、硬い | 実行時まで型エラーが分からない |
長らくこれは「どちらが優れているか」の宗教論争のように扱われてきました。しかし2020年代の答えは、 意外にも「両方とる」でした。
収斂 — 漸進的型付けという和解
近年の大きな潮流が漸進的型付け(gradual typing)です。これは、動的型付け言語に 「型注釈を後から、必要な部分だけ付け足せる」仕組みを導入するもの。動的型の柔軟さで素早く書き始め、 コードが成熟したら型を加えて安全性を高める——両者の良いとこ取りです。
最も成功した例が TypeScript(2012, Microsoft)です。動的型のJavaScriptに静的型を後付けし、
大規模開発に耐えうる安全性をもたらしました。同様に、Pythonもtype hintsと mypy による
型チェックがほぼ標準になり、Rubyにも型注釈の流れが広がっています。
// TypeScript: JavaScriptに型注釈を後付けする
function greet(name: string): string {
return `Hello, ${name}!`;
}
greet("World"); // OK
// greet(42); // コンパイル時にエラー: number は string に代入できない graph TD LC[λ計算\n1930s Church\n理論的基盤] LISP[LISP\n1958 最初の実装] ML[ML\n1973 型推論] HM[Hindley-Milner\n1978 型理論] HS[Haskell\n1990 純粋関数型の集大成] MULTI[主流言語へ流入\nScala/Rust/Swift/TS\nマルチパラダイム化] LC --> LISP LC --> ML ML --> HM HM --> HS ML --> HS HS --> MULTI LISP --> MULTI style LC fill:#3b82f6,stroke:#1d4ed8,color:#fff style HM fill:#8b5cf6,stroke:#6d28d9,color:#fff style HS fill:#f97316,stroke:#ea580c,color:#fff style MULTI fill:#14b8a6,stroke:#0d9488,color:#fff
λ計算
アロンゾ・チャーチ。関数の定義と適用だけで計算を表す数学的モデル。関数型の理論的基盤
LISP
マッカーシー。λ計算を最初に実装した言語。再帰・第一級関数・GC
ML
ロビン・ミルナー。LCF定理証明系のメタ言語。型推論を持つ最初の言語
Hindley-Milner型理論
ミルナーの論文。型推論アルゴリズムWと「型が正しければ実行時型エラーなし」の証明
Haskell
純粋関数型・遅延評価・型クラスの集大成。研究と実用を橋渡し
TypeScript / 漸進的型付け
動的言語に静的型を後付け。静的vs動的の対立は「両取り」へ収斂
理解度チェック
関数型プログラミングの理論的基盤となった、1930年代にアロンゾ・チャーチが考案した計算モデルは何ですか?
キーボード: 1〜4 で選択、Enter で回答