第3章で、構文の「見た目」と「式・文」の区別を学びました。本章では、その構文が何を体現しているのかに踏み込みます。 プログラミングにはパラダイム(paradigm)——「計算とは何か、どう記述すべきか」をめぐる根本的な様式——があり、 それぞれが固有の構文を持っています。命令型・オブジェクト指向・関数型・論理型・宣言型。同じ問題を解いても、 どのパラダイムで考えるかによって、コードの見た目はまったく変わります。

「パラダイム」という言葉の起源

まず言葉の出自から。「プログラミング・パラダイム」という用語を計算機科学に定着させたのは、 ロバート・フロイド(Robert W. Floyd)の1978年チューリング賞講演「The Paradigms of Programming」でした。 フロイドはこの「パラダイム」を、科学哲学者トマス・クーンの名著『科学革命の構造』(1962)から借用しました (クーンのパラダイム論そのものは第6章で深掘りします)。

五つのパラダイムと、それを映す構文

代表的なパラダイムを、「何を中心に世界を捉えるか」と「それを映す構文」で一覧にします。

パラダイム 世界の捉え方 それを体現する構文
命令型 / 手続き型 状態を一歩ずつ変化させる手順 代入文、for/while、手続き呼び出し
オブジェクト指向 状態を持つオブジェクト同士の対話 クラス定義、obj.method()(メッセージ送信)
関数型 値から値への変換(写像) 式、ラムダ、再帰、パターンマッチ、|>
論理型 成り立つ事実と規則の集合 事実 .、ルール :-、クエリ ?-
宣言型(狭義) 欲しい結果そのもの クエリ宣言(SQL)、タグ(HTML)

命令型 — 「どうやるか」を一歩ずつ

最古のパラダイムです。ノイマン型コンピュータの動作(メモリの値を順に書き換える)に素直に対応します。 中心にあるのは代入(状態の変更)ループ。「合計を求めよ」は「変数sumを用意し、 ループで足し込め」と手順として表現されます。

// 命令型(C)— 状態(sum)を一歩ずつ変化させる
int sum = 0;
for (int i = 0; i < n; i++) {
    sum = sum + arr[i];   // 代入で状態を更新
}

オブジェクト指向 — メッセージを送り合う

状態と振る舞いをオブジェクトにまとめ、オブジェクト同士がメッセージをやり取りして 計算を進めます。構文上は レシーバ.メソッド(引数) という形——「accountに『1000を入金せよ』というメッセージを送る」—— が象徴です。起源は1967年のSimula 67(クラスと継承を初めて導入し、ダールとニガードが2001年にチューリング賞)に遡ります。

// OOP(Java)— account に deposit メッセージを送る
class BankAccount {
    private int balance;                       // 状態のカプセル化
    public void deposit(int amt) { balance += amt; }  // メッセージハンドラ
}
account.deposit(1000);   // レシーバ . メソッド(引数)

関数型 — 値から値への変換

状態の変更を避け、計算を関数(値から値への写像)の合成として捉えます。理論的基盤は、 アロンゾ・チャーチが1930年代に作ったラムダ計算。中心となる構文は、ループの代わりの再帰(第3章の式指向)、高階関数(関数を引数や戻り値にする)、そしてパターンマッチです。

-- 関数型(Haskell)— 代入もループもない。式の合成だけ
-- 「リストを偶数だけにし、2倍し、合計する」を宣言的に書く
result = sum . map (*2) . filter even $ [1, 2, 3, 4, 5]
-- filter even → [2,4]、 map (*2) → [4,8]、 sum → 12

sum . map (*2) . filter even は関数を . で合成しています。「どんな手順で」ではなく 「どんな変換を施すか」を記述する——この発想が、後の章で見る関数型機能の主流言語への流入(第7章)の核心です。

論理型 — 事実とルールを宣言する

もっとも毛色が異なるのが論理型です。代表はProlog(1972、コルメラウアーら)。プログラマは 「成り立つ事実」と「規則」を宣言するだけで、「どう探索するか」はシステムに委ねます。 ロバート・コワルスキの有名な定式が 「アルゴリズム = 論理 + 制御」で、論理型ではプログラマが論理だけを書きます。

% 論理型(Prolog)— 事実とルールを宣言する
parent(tom, bob).                          % 事実: tom は bob の親
parent(bob, ann).                          % 事実
ancestor(X, Y) :- parent(X, Y).            % ルール: 直接の親は先祖
ancestor(X, Y) :- parent(X, Z), ancestor(Z, Y).  % ルール: 間接の先祖

?- ancestor(tom, ann).   % クエリ: → true(探索はシステムが行う)

Prologの計算エンジンの核は単一化(unification)——変数を含む項どうしを一致させて変数に値を束縛する操作で、 パターンマッチをさらに一般化したものです。命令型のような「手順」がどこにも書かれていないことに注目してください。

宣言型 — 「何が欲しいか」だけ

狭義の宣言型は、SQL・HTML・CSSのように「何を(WHAT)」だけを記述し、「どうやって(HOW)」をシステムに任せる言語です。 関数型も論理型も広義には宣言型に含まれますが、ここではより純粋な例を挙げます。

-- 宣言型(SQL)— 欲しいデータの条件を述べるだけ
SELECT name, salary
FROM employees
WHERE department = 'Engineering'
ORDER BY salary DESC;
-- 「どのインデックスを使い、どう結合するか」は一切書かない(DBが決める)

パターンマッチと代数的データ型 — 関数型の贈り物

ここで、現代の言語に広がりつつある重要な構文を取り上げます。パターンマッチ代数的データ型(ADT)です。 これらはML(1973)やHaskellといった関数型言語が確立し、いまやRust・Scala・Swift、さらにはJavaやPythonにも流入しています(第7章)。

パターンマッチは、しばしば「switch文の強化版」と誤解されますが、本質的に別物です。違いを正確に押さえましょう。

観点 命令型の switch/case 関数型のパターンマッチ
マッチ対象 値(スカラー)の一致 構造・型・コンストラクタ
変数束縛 なし あり(分解して取り出す)
網羅性チェック なし あり(漏れをコンパイラが検出)
式か文か 多くは文 (値を返す)

代数的データ型は「いくつかの種類のいずれか」(直和型)と「複数の値の組」(直積型)を組み合わせて データの形を定義します。パターンマッチはその形に沿ってデータを分解し、各ケースを処理します。 Rustの例を見てください。

// 代数的データ型(直和型)の定義
enum Message {
    Quit,                        // 値なし
    Move { x: i32, y: i32 },     // 直積(複数フィールド)
    Write(String),               // 値あり
}

// パターンマッチ — 構造で分岐し、中身を変数に束縛する
fn process(msg: Message) {
    match msg {
        Message::Quit          => println!("終了"),
        Message::Move { x, y } => println!("移動: ({x}, {y})"),  // x,y を束縛
        Message::Write(text)   => println!("書き込み: {text}"),  // text を束縛
    }   // ← もしケースを書き忘れると、コンパイルエラーになる(網羅性保証)
}

同じ問題、五つの思考様式

締めくくりに、「整数リストの偶数だけを2倍して合計する」という同じ問題を各パラダイムで書き並べます。 構文がそのまま思考様式を映し出していることを感じ取ってください。

graph LR
  P["問題:\n偶数を2倍して合計"]
  IMP["命令型\nループで足し込む"]
  FUN["関数型\nfilter→map→sum"]
  LOG["論理型\n関係を宣言"]
  DEC["宣言型\nSELECT SUM"]
  P --> IMP
  P --> FUN
  P --> LOG
  P --> DEC
  style FUN fill:#8b5cf6,stroke:#6d28d9,color:#fff
  style DEC fill:#14b8a6,stroke:#0d9488,color:#fff
同じ問題でも、パラダイムによって「手順を書く」のか「変換を書く」のか「関係を宣言する」のかが分かれる
# 命令型(Python)— 手順
total = 0
for x in [1,2,3,4,5]:
    if x % 2 == 0:
        total += x * 2          # total = 12

# 関数型(Python)— 変換の合成
total = sum(x * 2 for x in [1,2,3,4,5] if x % 2 == 0)   # 12

命令型は「変数を用意し、ループで足し込む手順」を書きます。関数型は「偶数だけにして、2倍して、合計する変換」を書きます。 同じ答えにたどり着くのに、頭の使い方がまるで違う。これこそ、フロイドが「パラダイム」という言葉で捉えようとしたものでした。

まとめ — 構文は様式の鏡

本章では、フロイド(1978)が定着させた「パラダイム」概念から出発し、命令型・OOP・関数型・論理型・宣言型という 五つの様式と、それぞれを体現する構文を見ました。アラン・ケイの「本質はメッセージング」、関数型の式と再帰、 論理型の事実とルール、宣言型のWHAT中心。そしてパターンマッチと代数的データ型が、単なるswitch文ではなく 「構造で分解し、網羅性を保証する」関数型由来の強力な構文であることを学びました。

次章では視点を変え、構文が「書きやすさ・表現力」をどう作るのかに注目します。糖衣構文と脱糖、 そしてマクロやDSLという「構文そのものを拡張する」技法へ——第1章で出会ったランディンの「甘味料」が再び主役になります。

理解度チェック

問題 0 / 50%
Q1

「プログラミング・パラダイム」という用語を計算機科学に定着させたのは誰で、どこから概念を借用しましたか?

キーボード: 1〜4 で選択、Enter で回答