2026年6月1日月曜日

オブジェクト指向はなぜ生まれたのか?【プログラミング言語の歴史】- - YouTube動画の解説

 

ご提示いただいたYouTube動画「オブジェクト指向はなぜ生まれたのか?【プログラミング言語の歴史】」(チャンネル名:ゆっくり情報科学ちゃんねる)の内容を要約し、プログラミングにおける「オブジェクト指向」の概念や歴史、業界の裏話・雑学を交えて分かりやすく解説します。

🎥 動画の全体要約

この動画は、現代のソフトウェア開発の根幹にある「オブジェクト指向プログラミング(OOP)」が、どのような背景(危機)から生まれ、どのように進化し、世界中に普及したのかを、1960年代から現代に至るまでの歴史を追いながら解説しています。

単に技術的な用語を説明するだけでなく、「現実の課題を解決するために、先人たちがどうもがいてきたか」というドラマとしてオブジェクト指向を描いているのが特徴です。

1. オブジェクト指向が生まれた背景:業界話と「ソフトウェア危機」

オブジェクト指向が生まれる前、プログラミングは手順を順番に書いていく「手続き型」が主流でした。しかし、1960年代後半に「ソフトウェアクライシス(ソフトウェア危機)」と呼ばれる大問題が発生します [01:28]。

  • 業界話・歴史の裏話:

    当時、IBMの「OS/360」開発などにおいて、システムが大規模化するにつれて「納期も予算も絶対に守れない」「1箇所を直すと別の場所がバグる」という地獄のような状況に陥りました [01:34, 01:53]。コードが1万行を超えると、保守するより最初から作り直した方が早いとさえ言われた暗黒時代です [02:48]。

    この「混沌とした巨大なコードをどう管理するか」という絶望への答えとして、オブジェクト指向(データと処理をひとまとめにする思想)が浮上しました [02:06]。

2. オブジェクト指向の出発点:船のシミュレーション(雑学)

オブジェクト指向の元祖は、1960年代にノルウェーの海運国としてのニーズから生まれた「Simula(シミュラ)」という言語です [00:40, 08:43]。

  • おもしろ雑学:

    開発者のダールとナイガードは、「港の渋滞や船の衝突を予測するためのシミュレーション」を行いたいと考えていました [08:54]。しかし、当時の手続き型言語では「船」「港」「クレーン」といった現実のモノをうまく表現できませんでした。そこで、「現実のモノ(状態と行動を持つ)を、そのままコードの中に再現しよう」と考えたのが、オブジェクト指向の本当の始まりです [09:11, 09:21]。

    ※ちなみに、この2人は2001年に計算機科学の最高栄誉「チューリング賞」を受賞しましたが、その翌年に相次いで亡くなっています [10:29]。

3. オブジェクト指向の「3本柱」とは?

オブジェクト指向を支える、特に重要な3つの概念です。

  1. カプセル化 (Encapsulation) [04:34]

    • データと処理をセットにして、外から勝手にデータを書き換えられないように保護する仕組み [04:54]。

    • 例: 銀行口座オブジェクトの「残高」を外から直接マイナス100万円に書き換えられたら困るので、必ず「引き出しメソッド」という窓口(カプセル)を通してしか操作できないようにします [05:00]。

  2. 継承 (Inheritance) [05:17]

    • すでにあるクラス(設計図)の機能を引き継いで、新しいクラスを作ること [05:17]。

    • 例: 「乗り物」という基本クラスから、「車」や「バイク」のクラスを作ることで、共通するコードを何度も書かずに済みます [05:22]。

  3. ポリモーフィズム(多態性)(Polymorphism) [05:42]

    • 同じ命令を送っても、受け取る相手(オブジェクト)によって異なる動きをする性質 [05:45]。

    • 例: 「鳴け」というメッセージを送ったとき、犬のオブジェクトなら「ワン」、猫なら「ニャー」と鳴きます [05:50]。呼び出す側は相手が「何の動物か」を細かく気にしなくてよいため、大規模開発が劇的に楽になりました [05:54]。

4. アラン・ケイと「オブジェクト指向」という言葉の誕生

「オブジェクト指向プログラミング(OOP)」という言葉を実際に作ったのは、アラン・ケイという天才研究者です(1972年頃、伝説のゼロックス・パロアルト研究所にて「Smalltalk」を開発) [13:51]。

  • 業界の裏話(本質とのズレ):

    アラン・ケイが理想としたのは、オブジェクト同士が「メッセージパッシング(メッセージのやり取り)」で自律的に動く世界でした(「あれをやれ」と命令するのではなく「これをお願いします」とメッセージを投げるイメージ) [15:05]。 しかし後世の言語(C++やJavaなど)は、メッセージのやり取りよりも「クラスの階層構造や継承」を重視して普及していきました。そのため、アラン・ケイは後に「私がオブジェクト指向と呼んだものは、C++やJavaのようなものではない」と苦言を呈しています [15:44]。

5. 世界への普及:C++ と Java の功績

概念としては素晴らしかったオブジェクト指向ですが、初期のSimulaなどは「実行速度が遅すぎる」という致命的な欠点があり普及しませんでした [10:10]。それを実用レベルに引き上げたのが以下の2つの言語です。

  • C++(1980年代〜):

    開発者のストラウストラップが「Simulaのオブジェクト指向」と「C言語の圧倒的な速さ」を合体させて作りました [17:17, 17:38]。これによって「オブジェクト指向は実用になる」と世界が気づきます [12:10]。

  • Java(1995年〜):

    C++の難点だった「複雑なメモリ管理(解放を忘れるとシステムがクラッシュする)」を、ガベージコレクション(GC)という自動掃除機能で解決しました [19:24, 19:33]。また、すべてのコードにオブジェクト指向を強制することで、誰が書いても一定の品質になる設計の標準化をもたらし、インターネットの爆発的普及(Webブーム)の波に乗って大流行しました [18:50, 19:06]。

6. 設計の教科書:デザインパターン(業界話)

オブジェクト指向が普及すると、今度は「道具は揃ったが、どう書けば『良い設計』になるのか?」が問題になりました。そこで1994年、4人のエンジニア(通称 GoF: Gang of Four)が、優れた設計の共通パターンを23個にまとめた書籍を発表します [20:54, 21:05]。

  • 業界話・エンジニアの罠:

    デザインパターンの登場によって、エンジニア同士が「そこはシングルトンパターンでいこう」「オブザーバーパターンで通知しよう」と共通言語で会話できるようになり、開発効率が爆発的に上がりました [21:26, 21:40]。

    しかし、一方で「パターン信者」と呼ばれる、シンプルに書けばいい場所にあえて複雑なデザインパターンを無理やり当てはめてコードを読みにくくする「知識の罠」にハマる開発者も続出しました [24:03, 24:14]。

  • ちなみに:

    このGoFの1人であるエリック・ガンマは、後にマイクロソフトに移籍し、現在世界中のエンジニアが愛用しているコードエディタ「VS Code(Visual Studio Code)」の開発をリードしています [24:48, 25:04]。

7. 現代におけるオブジェクト指向への批判と進化

現在、オブジェクト指向は完璧な万能薬ではなく、いくつかの弱点も指摘されています。

  • 「バナナとゴリラ」問題:

    Erlangの生みの親ジョー・アームストロングは、「バナナ(必要な機能)を食べたいだけなのに、継承を使うと、バナナを持っているゴリラと、それが住んでいるジャングル全体(不要な親クラスの機能)まで付いてきてしまう」と、過度な「継承」の不自由さを批判しました [26:12, 26:20]。

  • 平行処理との相性の悪さ:

    「状態を書き換える(ミュータブル)」というオブジェクト指向の性質は、現代のマルチコアCPUによる同時並行処理において、データの衝突を招きやすくバグの原因になりやすいと指摘されています [26:39]。

こうした反省から、現代では「ソリッド原則(SOLID)」などのより洗練された設計指針が作られたり、RustやGoのようにあえて「継承」を廃止してシンプルな組み合わせ(コンポジション)を優先する言語が主流になりつつあります [27:12, 27:52]。

💡 結論

動画の締めくくりとして、現代は「オブジェクト指向」か「関数型」かという二者択一ではなく、両方の良いところを組み合わせるマルチパラダイムの時代であると述べられています [29:54]。しかし、オブジェクト指向が作った「モノを中心として設計を語る共通言語」は、誕生から60年が経った今でも世界中の現場で毎日使われ続けています [29:59, 30:12]。

ご提示の動画要約は非常に良くまとまっています。 ここでは、その内容を基に補足・補強しつつ、追加の雑学・業界話・技術的背景を交えて、より深く分かりやすく解説します。歴史の流れを追いながら、なぜOOPが「必要とされたか」「どう歪んだか」「今どう位置づけられているか」を整理します。

1. ソフトウェア危機(Software Crisis)とOOP誕生の必然性

1960年代後半〜1970年代、ソフトウェア危機は本当の意味で「産業崩壊レベルの危機」でした。IBMのOS/360プロジェクトは象徴的で、開発費が当初予定の数倍、納期遅延、欠陥だらけという地獄絵図でした。コードが数百万行規模になると、人間が全体を把握できなくなり、「1箇所直したら他が壊れる」スパゲッティコードが横行。

業界の裏話:当時のプログラマは「英雄的個人プレイ」が主流で、 genial なハッカーが神業でシステムを動かしていましたが、大規模化でそれが通用しなくなった。OOPは「個人の天才性に頼らない、凡人でも管理可能な構造」を目指した現実的な回答だったと言えます。

2. オブジェクト指向の原点:Simulaと「現実世界のモデリング」

ノルウェーのオーレ=ヨハン・ダールとクリステン・ニガードが開発したSimula(1960年代後半)が起源です。彼らは港湾シミュレーションで「船」「港」「クレーン」などの実体(entity)をそのままコードで表現したかった。手続き型では「データ」と「処理」がバラバラで、現実の「モノ」の振る舞いを再現しにくかったのです。

面白い雑学

  • SimulaはALGOLの拡張として作られ、クラス(class)継承(inheritance)の概念を世界で初めて導入。
  • 2人は2001年にチューリング賞を受賞しましたが、翌2002年に相次いで亡くなっています(動画通り)。OOPの父たちは晩年に栄誉を得た形です。
  • Simula自体は実行速度が遅くニッチでしたが、思想が爆発的に広がったきっかけになりました。

3. OOPの3本柱(+α)

動画の説明が正確です。補足すると:

  • カプセル化:データ隠蔽(information hiding)。David Parnasが提唱した考えが基盤。
  • 継承:コード再利用の強力な手段だが、後述するように「諸刃の剣」。
  • ポリモーフィズム:これがOOPの真の威力。「呼び出す側が相手の詳細を知らなくてよい」という抽象化が、大規模チーム開発を可能にした。

追加の柱としてよく語られるもの抽象化(Abstraction)。現実の複雑さを適切に隠す力です。

4. アラン・ケイとSmalltalk:理想と現実の乖離

アラン・ケイ(Xerox PARC)が「オブジェクト指向」という言葉を普及させ、Smalltalkで体現しました。彼のビジョンは「生物の細胞」や「インターネットのような分散オブジェクト」——オブジェクト同士がメッセージパッシングで自律的に協調する世界でした。

重要な業界話

  • ケイ本人は後年、「C++やJavaは私が言ったオブジェクト指向ではない」と繰り返し批判。「私が考えていたのは生物学的・並行性の強いモデルだった」と。
  • SmalltalkはGUI(ウィンドウ、メニューなど)の開発で革命を起こしましたが、商用化のタイミングを逃しました。PARCの技術はAppleのMacintoshや後にWindowsに影響を与えました。

5. 実用化:C++とJavaの役割

  • C++(Bjarne Stroustrup, 1980年代):Cの速度を保ちつつSimulaのアイデアを導入。「C with Classes」から始まりました。ゲーム、組み込み、ハイパフォーマンス分野で今も強い。
  • Java(James Gosling, 1995年):C++のメモリ管理の煩雑さ(ポインタ、解放忘れによるクラッシュ)をGC(ガベージコレクション)で解決。「Write Once, Run Anywhere」でWeb時代にマッチ。企業システム(エンタープライズ)の標準になりました。

雑学:Javaは当初「Oak」という名前で、家電組み込み用として開発されていました。インターネットブームで方向転換した幸運な言語です。

6. デザインパターン(GoF)とその光と影

1994年の『Design Patterns: Elements of Reusable Object-Oriented Software』(GoF:Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides)はOOP設計の「聖書」になりました。23のパターンを分類(Creational, Structural, Behavioral)。

補足

  • Erich Gamma(GoFの一人)は後にMicrosoftに移り、VS Codeの初期開発に大きく貢献しました(動画の情報通り)。
  • 業界の罠:パターン信者による過剰適用。「FactoryFactory」みたいな無意味に複雑なコードが生まれる。現代では「YAGNI (You Ain't Gonna Need It)」「シンプル第一」の考えが強くなっています。

7. 現代の批判と進化

  • バナナとゴリラ問題(Joe Armstrong, Erlang作者):継承の弊害を完璧に表現。「バナナだけ欲しいのに、ゴリラ(親クラス)とジャングル(依存関係)全部持ってくる」。
  • ミュータブル状態の問題:並行処理(マルチコア)でロック競合・デッドロックが発生しやすい。
  • 解決策の潮流
    • Composition over Inheritance(継承より構成):Go言語やRustが好例。
    • SOLID原則(Robert C. Martin):Single Responsibilityなど、OOPをより健全に使う指針。
    • マルチパラダイム:Python(OOP+関数型)、Rust(所有権システムで安全)、Kotlinなど。

結論:OOPは「終わった」わけではない

OOPは「大規模開発における共通言語と構造化の枠組み」として、今も極めて有効です。特にドメイン駆動設計(DDD)では、オブジェクトを「現実のビジネス概念」に近づける考え方が生きています。

ただし、アラン・ケイの純粋なビジョン(メッセージング中心の分散オブジェクト)は、マイクロサービスやアクターモデル(Akkaなど)で部分的に実現されていると言えます。現代の賢い開発者は「OOPを盲信せず、適切に使う」——関数型の純粋性、データ指向、コンポジションを組み合わせています。

動画が伝えたかった「先人たちの苦闘と創意工夫」は、まさにプログラミングの本質です。OOPを学ぶときは「なぜこの仕組みが生まれたか」を常に意識すると、コードが格段に良くなります!

さらに具体的な言語例やデザインパターンの実装例が必要でしたら、教えてください。

オブジェクト指向プログラミング(OOP)の歴史と思想

この動画は、OOPの起源から現代まで、歴史の流れに沿って解説しています。順を追って詳しく説明します。


1. なぜOOPが必要だったのか ― ソフトウェアクライシス(1968年)

手続き型プログラミングの限界

当時のプログラムは「手順の列」として書かれていました。コードが大規模になると:

  • グローバル汚染:関数名が print, print2, printFinal のように衝突し、名前管理が崩壊
  • データの野放し:誰でもデータを直接書き換えられるため、「どこで何が変わったか」が追えなくなる
  • 修正の連鎖崩壊:1か所直すと別の場所が壊れる

IBMのOS/360プロジェクトはその象徴で、後にフレデリック・ブルックスが**「人月の神話」**で記録した大規模失敗案件です。

構造化プログラミングとの関係

ダイクストラが1968年に「goto文は有害」という論文を発表し、ifやループで制御フローを整理しました。しかしデータ管理は未解決のままでした。そこにOOPが登場します。


2. OOPの3本柱

① カプセル化(Encapsulation)

データと処理を一まとめにし、外から直接触れないようにする仕組み。

java
// 悪い例(手続き型的)
balance = -100; // 誰でも直接書き換えられる

// 良い例(カプセル化)
account.deposit(100);  // メソッド経由でしか変更できない

public / private / protected のアクセス修飾子で制御します。

② 継承(Inheritance)

既存クラスの機能を引き継いで新しいクラスを作る仕組み。

java
class Vehicle { void move() { ... } }
class Car extends Vehicle { ... }  // moveを引き継ぐ

ただし後述するように、乱用が問題になります。

③ ポリモーフィズム(Polymorphism)

同じメッセージを送っても、受け取るオブジェクトによって動作が変わる。

java
animal.speak(); // 犬→「わん」、猫→「にゃあ」
// 呼ぶ側は動物の種類を気にしなくていい

これが大規模開発を劇的に楽にした核心です。

抽象クラスとインターフェース

  • 抽象クラス:不完全なクラス。実装を強制する
  • インターフェース:実装を持たない「約束ごと」だけを定義

→ ポリモーフィズムをさらに柔軟にする仕組みです。


3. 起源 ― ノルウェーの船のシミュレーション

シミュラ(Simula)1962〜1967年

開発者:オーレ=ヨハン・ダールクリステン・ニガード(ノルウェー計算機センター)

動機:港での船の渋滞・衝突を事前にシミュレーションしたかった。 当時の手続き型言語では「船・港・クレーンがそれぞれ独自の状態と行動を持つ」という現実を表現できなかったのです。

  • 1962年:Simula I 作成
  • 1967年:クラスの概念を本格搭載した Simula 67 完成

なぜ広まらなかったのか?

  • 速度問題:同じ処理がCより10倍以上遅いこともあった
  • 環境の限界:施設に1台の大型機、パンチカード入力の時代
  • 情報の流通:ノルウェーの研究機関内に留まり、世界に届くのに時間がかかった

その後

  • 2001年、チューリング賞受賞(コンピュータ科学のノーベル賞)
  • しかし翌年2002年、ダールが6月、ニガードが8月に相次いで逝去
  • コルーチン(処理を途中で止めて後で再開する仕組み)もシミュラの発明で、今のasync/awaitの先祖です

「目の前の問題を解こうとした人が世界を変えてしまった」


4. スモールトーク(Smalltalk) ― OOPという言葉の誕生

アラン・ケイとゼロックスPARC(1972年〜)

  • 「オブジェクト指向プログラミング」という言葉を作ったのはアラン・ケイ
  • ゼロックスPARCはGUI・イーサネットを生んだ伝説の研究所

スモールトークの核心思想

  • 全てがオブジェクト:数値も文字列も、クラスを定義する行為自体も
  • メッセージパッシング:「やれ」ではなく「お願いします」という設計哲学。受け取った側が自律的に応じる方法を決める

アラン・ケイの本来の構想

ケイはプログラミング言語ではなく、コンピューターのための新しいメディアとして構想していました(ダイナブック構想:子供が持ち歩けるパーソナルコンピューター、1972年)。

重要な逆説:ケイ自身が「私がオブジェクト指向と呼んだものはC++でもJavaでもない」と言っています。本来の核心はメッセージパッシングでしたが、後の言語はクラスと継承を中心に据えてしまいました。


5. C++ ― 概念の実用化(1979〜1985年)

開発者:ビャーネ・ストロヴストルップ(ベル研究所)

動機:博士論文でシミュラを使ったら遅すぎた → シミュラの思想 × Cの速度を両立したい

要素 由来
クラス・継承・仮想関数(ポリモーフィズム) Simula
実行速度 C
  • 1979年:C with Classes として開発開始
  • 1983年:C++ に改名(++はCのインクリメント演算子、「Cを1つ進めた」の意)
  • 1985年:公式コンパイラ公開

6. Java ― OOPの民主化(1995年)

開発:サン・マイクロシステムズ、ジェームズ・ゴスリングらのチーム

C++との違い

比較項目 C++ Java
移植性 OS別にコンパイルし直す必要あり JVM上でどこでも動く(Write Once, Run Anywhere)
メモリ管理 手動(解放忘れでリーク、二重解放でクラッシュ) ガベージコレクション(GC)で自動管理
設計の自由度 高い OOPを強制することで統一

GCのトレードオフ

GCにはストップ・ザ・ワールド問題があります。GCが動く瞬間、プログラム全体が一時停止します。ゲームや金融の高頻度取引では問題になるため、C++やRustを選ぶ開発者もいます。


7. デザインパターン(GoF、1994年) ― 設計の共通言語

著者:エリヒ・ガンマ、リチャード・ヘルム、ラルフ・ジョンソン、ジョン・ブリシディーズ(Gang of Four / 4人組

革命の本質:コードではなく設計を語る共通言語を作った。「ファクトリーパターンで作る」と言えば、詳しい説明なしに意図が伝わる。

主要パターン解説

パターン 概要 例え
シングルトン オブジェクトを1つしか作らない 全員が同じ設定ファイルを参照する
オブザーバー 変化を関係者に自動通知 LINEグループの通知 / Reactのstateの原点
ストラテジー やり方を後から差し替え可能 支払い方法をコードを変えずに切り替える
デコレーター 機能を後から追加 コーヒーにミルク・砂糖を足す

23パターンが「生成・構造・振る舞い」の3カテゴリーに収録されています。

GoFへの批判

「パターンのためのパターン」になる乱用問題。シンプルに書けば良いところに無理矢理パターンを当てはめる人が出てきます。

GoFの本は「このパターンを使え」ではなく「この問題には過去にこういう解決策があった」という知識の収集です。

その後

エリヒ・ガンマは2011年にMicrosoftに移り、Visual Studio Codeの開発を直接リードしました。


8. OOPへの批判と進化

主な批判

ジョー・アームストロング(Erlang作者)

「バナナを食べたいだけなのに、バナナを持つゴリラと、ゴリラのいるジャングル全体をもらった」

継承を使うと、必要な機能だけ取り出せず余計なものが全部ついてくる問題。

リッチ・ヒッキー(Clojure作者)

「ミュータブルな状態を中心に据えたOOPは、並行処理時代に根本的に向いていない」

関数型プログラミングはデータを**不変(イミュータブル)**にする発想で、状態が変わらないなら並行処理でも安全です。


9. SOLID原則 ― 正しいOOPの指針

**ロバート・マーティン(アンクルボブ)**が整理した5つの設計指針:

頭文字 原則 内容
S 単一責任原則 クラスが変更される理由は1つであるべき
O オープン/クローズド原則 拡張には開かれ、修正には閉じている
L リスコフ置換原則 サブクラスは親クラスと置き換えても正しく動くべき(バーバラ・リスコフ、2008年チューリング賞)
I インターフェース分離原則 使わないメソッドへの依存を強制しない
D 依存関係逆転原則 抽象に依存し、具体に依存しない

SOLIDは本質的に**「継承の使いすぎへの反省」から生まれた指針**です。


10. 現代 ― マルチパラダイムの時代

Simula (1967)
    ↓
Smalltalk (1972) ← "オブジェクト指向"という言葉の誕生
    ↓
C++ (1985) ← 実用化
    ↓
Java (1995) ← 民主化
    ↓
GoFデザインパターン (1994) ← 設計の言語化
    ↓
SOLID原則 ← 批判を経た精錬
    ↓
現代:Go(継承なし)、Rust(トレイト)、Python(ダックタイピング)
     → OOP + 関数型 のマルチパラダイム
  • Go:インターフェースと構造体だけで設計
  • Rust:トレイト(ミックスイン)はコンポジション優先
  • Python:ダックタイピング(「同じメソッドを持っていれば同じように扱う」)

まとめ

「世界を変えようとして作るより、目の前の問題を解こうとした人が世界を変えてしまう」

1960年代に船のシミュレーションをしたかっただけのノルウェーの2人の研究者が生み出した概念が、60年後に世界中のソフトウェアの設計哲学になりました。OOPは批判を受けながらも否定ではなく精錬を繰り返し、今も「オブザーバー」「ストラテジー」という共通言語として毎日世界中で使われています。

2026年5月28日木曜日

コンパイラはなぜ生まれたのか【プログラミング言語の歴史】- - YouTube動画の解説

ご提示いただいた動画は、YouTubeチャンネル「ゆっくり情報科学ちゃんねる」の「コンパイラはなぜ生まれたのか【プログラミング言語の歴史】」です。

この動画では、プログラミング言語がアセンブリ言語(低級言語)からコンパイラを必要とする高級言語へと進化していった歴史、コンパイラの内部の仕組み、そして様々な雑学や業界話が「ゆっくり(魔理沙と霊夢)」の掛け合いで非常に深く解説されています。

動画の内容をベースに、コンパイラ開発の基礎や逸話、知っておくと面白い業界の裏話(雑学)を交えて分かりやすく要約・解説します。

1. コンパイラ誕生前夜:アセンブリ言語という名の「地獄」

1950年代後半、プログラマーたちは機械語の一歩手前である「アセンブリ言語」を使っていました [00:17]。しかし、プログラムの規模が10万行を超えるようになると、そこはまさに地獄の環境でした [01:21]。

  • 人間CPUデバッグ [03:15]

    当時はバグが出ると、メモリのアドレスやレジスタ(CPUの記憶領域)の中身をすべて紙に書き出し、人間が頭の中でCPUの動きを1ステップずつ再現してデバッグしていました [03:21]。

  • レジスタ競合(割り当て)のパズル [03:39]

    CPUが計算に使う「レジスタ」は数が限られています。複数の処理が同じレジスタを奪い合わないよう、どの処理にどのレジスタを割り当てるかを、人間がすべて脳内で管理しなければなりませんでした [03:45]。

  • コードは完全に「使い捨て」 [04:13]

    アセンブリ言語はCPUの構造に1対1で依存するため、IBMの機種向けに書いたコードはユニバックなど他社の機種(命令セットが異なる機種)では1行も動きません [04:02]。会社が新しいコンピュータを導入するたびに、それまでのプログラムをすべて捨ててゼロから書き直していました [04:13]。

2. 歴史を変えた「先駆者たち」の逸話と雑学

逸話①:コンパイラの母「グレース・ホッパー」 [05:35]

1952年に「A-0システム」を作り、初めて「コンパイラ」という言葉を使ったのが、アメリカ海軍の数学者でありプログラマーでもあったグレース・ホッパーです [05:35]。ただし、彼女の作ったものは「あらかじめ用意されたプログラムの部品(サブルーチン)を組み合わせる」仕組み(現代でいうローダーやリンカーに近いもの)だったため、「これが世界初のコンパイラか?」については現在も議論があります [05:49]。しかし、彼女が「翻訳を機械にやらせる」という概念の先駆者であることは間違いありません [06:13]。

逸話②:フォートラン(Fortran)と職人たちのプライド [06:55]

1957年、IBMのジョン・バッカスたちが発表した「フォートラン」が、現代的な意味で広く認められた世界初の高級言語・コンパイラです [06:55]。

  • 業界の猛反発 [10:01]: 当時、プロのプログラマーたちは「機械が自動生成したコードなんか、人間の手書きアセンブリ(職人技)より遅いに決まっている。信用できない」と猛反発・猛反対しました [10:01]。

  • 批判を黙らせた「ループ最適化」 [07:44]: バッカスのチームは3年を費やし [07:23]、計算の順番を賢く組み替えてレジスタを最大効率で使い回す技術(依存解析)を開発 [08:13]。人間の職人が書いたコードと同等以上の爆速コードを吐き出すコンパイラを完成させ、結果で批判派を黙らせました [10:20]。これにより、数ヶ月かかっていた開発が数週間に短縮されるという「プログラミングの民主化」が始まりました [09:17]。

3. コンパイラがやっていること(開発の基礎)

コンパイラは、人間が書いたソースコードを機械語に翻訳する「工場」のようなものです。大きく分けて以下の5つの工程(パイプライン)で動いています [10:55]。

[ソースコード] 
      ↓
① 字句解析 (文字列を単語/トークンに分ける) [00:11:10]
      ↓
② 構文解析 (文法をチェックし、木の構造「AST」にする) [00:11:57]
      ↓
③ 意味解析 (「文字と数値を足そうとしていないか」等のエラーチェック) [00:12:44]
      ↓
④ 最適化   (ここが職人技!無駄なコードを削り、爆速にする) [00:13:09]
      ↓
⑤ コード生成 (特定のCPUが理解できる機械語にする) [00:16:09]

コンパイラ開発者の「変態的な最適化テクニック」

コンパイラが裏でやってくれている、人間顔負けの最適化雑学です。

  • 定数畳み込み [13:13]: コードに 3 + 5 と書いてあったら、実行する前にコンパイラが勝手に 8 に計算し直して埋め込みます [13:17]。

  • デッドコード除去 [13:30]: 絶対に通らない条件分岐や、一度も呼び出されない関数を見つけて、自動的に削除(掃除)します [13:34]。

  • ループ不変式移動 [13:51]: ループの中で毎回同じ結果になる計算(例:円周率×半径の2乗など)がある場合、それを自動的にループの外に引っ張り出して、計算回数を1回に減らします [13:56]。

  • インライン展開 [14:31]: 短い関数を呼び出す際、呼び出し処理のオーバーヘッド(スタックへのメモリ退避など)を無くすため、関数の内容を呼び出し元にそのままドバッと埋め込みます [14:35]。

4. 知ると面白いコンパイラ・プログラミング業界話(雑学)

雑学①:鶏と卵の「セルフホスティング」 [30:11]

「C言語のコンパイラはC言語で書かれている」という有名な話があります。では、一番最初のC言語コンパイラはどうやってコンパイルしたのでしょうか?

これをブートストラップ問題といいます [30:31]。元々、C言語の最初のコンパイラは「B言語」という別の言語で作られました [30:15]。そのC言語コンパイラを使って、「C言語で書かれたC言語コンパイラのコード」をコンパイルすることで、最終的に「自分自身で自分を作れる状態(セルフホスティング)」に到達したのです [30:19]。言語が自立した、生命の自己複製のような歴史的瞬間です [30:26]。

炸裂する中間表現(IR)とLLVMの革命 [16:13]

昔は「新しい言語」を1つ作り、「CPU(x86、ARMなど)」にそれぞれ対応させようとすると、言語×CPUの数だけ翻訳機をゼロから作る必要がありました。

これを解決したのがLLVMという現代のコンパイラ基盤です [35:17]。

ソースコードを一度、特定のCPUに依存しない「中間表現(IR)」に翻訳します [16:13]。LLVMは、このIRを各CPUの機械語に直す役割(バックエンド)を一手に引き受けてくれます [35:44]。そのため、現代の言語開発者は「自分の言語をIRに変換する部分(フロントエンド)」だけを作れば、最初からあらゆるCPUに対応した超高性能な言語をリリースできるようになりました(RustやSwift、Appleの基盤もこれに乗っかっています) [35:44]。

雑学③:JavaやJavaScriptを爆速にした「JIT(ジット)」 [36:30]

コンパイラ(事前に全部翻訳する)とインタープリタ(実行時に1行ずつ翻訳する)の良いとこ取りをしたのがJIT(実行時コンパイル)です [36:34]。

プログラムを実行しながら「ここ、何回も通る重いループだな(ホットスポット)」と監視・検知し、実行中にその場でその部分だけをピンポイントで超強力に機械語へコンパイルします [36:44]。

Googleの「V8エンジン」がJavaScriptにJITを搭載したことで、ブラウザ上の動作スピードがそれまでと比べ物にならないほど爆速になり、現在の「Googleマップ」などのリッチなWebアプリがサクサク動く世界が作られました [38:06]。

まとめ

コンパイラの歴史の本質は、「人間がCPU(機械)の細かい都合を気にせず、解決したい問題の本質だけに集中できるようにするための『抽象化』の歴史」です [19:27]。

かつて「機械の生成したコードなんか信用できない」と言われていたコンパイラは、70年以上の研究と理論(グラフ理論や連立方程式のような型推論など)の積み重ねによって [18:27, 34:26]、今や人間が手で書くよりも遥かに安全で、最適化されたコードを眠らずに吐き出し続ける、ソフトウェア産業最大の功労者となっています [24:07]。

(動画URL: https://www.youtube.com/watch?v=_K8htW_mZZQ

コンパイラの歴史と仕組みを、動画内容を基に深掘り・補完解説します。

ご提示の動画要約は非常に良くまとまっています。ゆっくり解説らしい親しみやすい語り口を保ちつつ、歴史的事実の補足、追加の業界雑学、技術的な深掘りを加えて解説します。

1. コンパイラ誕生前夜:アセンブリ言語の「地獄」

1950年代後半、アセンブリ言語は機械語(0と1の羅列)を人間が読みやすくした「ニーモニック」でしたが、確かに地獄でした。

  • 人間CPUデバッグ:紙にメモリダンプを印刷し、手でCPUの状態をシミュレート。現代のデバッガ(GDBなど)があれば数秒で済む作業が、数時間〜数日かかっていました。
  • レジスタ割り当てのパズル:当時のCPU(例: IBM 704)はレジスタが極端に少なく(アクセラレータレジスタなど数個)、人間が「この変数はここ、この一時計算はあそこ」と脳内管理する必要がありました。最適化の失敗で即座にバグや遅延が発生。
  • 移植性の絶望:命令セットアーキテクチャ(ISA)が異なれば完全に別物。1社で複数機種を使っていた企業は、プログラムを「機種ごとに完全書き直し」していました。これがソフトウェアコストの爆増を招き、コンパイラ誕生の最大の原動力になりました。

追加雑学:この時代、プログラマーは「職人」として神聖視されていましたが、実際は「機械の奴隷」状態でした。プログラムの規模が大きくなると、人間的限界が露呈したのです。

2. 先駆者たちの逸話と雑学

逸話①:グレース・ホッパーとA-0システム(1952年) ホッパーは「コンパイラ」という言葉を普及させた人物で正しいですが、A-0は厳密には現代のコンパイラというよりリンカ/ローダー寄りのツールでした。サブルーチン(部品)をテープから呼び出して組み合わせ、機械語に変換する仕組みです。

彼女の功績は「機械に翻訳を任せる」という思想の普及にあります。海軍でCOBOLの基盤(FLOW-MATIC)も開発し、「バグ(bug)」という言葉を有名にした人物としても知られています。

逸話②:Fortran(1957年)と職人たちの反発 John Backus率いるIBMチームが開発。世界初の「実用的」高級言語+最適化コンパイラです。

  • 猛反発の背景:プロのプログラマーは「自分の手書きアセンブリより遅くなるはず」と信じていました。これは「職人プライド」と「ジョブセキュリティ」の問題でもありました。
  • 逆転の鍵:依存解析とループ最適化で、人間並みかそれ以上の性能を実現。結果、数ヶ月かかっていた科学計算プログラムの開発が数週間に短縮され、「プログラミングの民主化」が始まりました。

追加雑学:Backusは後にALGOLの文法定義に使われるBNF(Backus-Naur Form)も考案。Fortranは今でも気象予報や数値シミュレーションで現役です。

3. コンパイラの内部構造(5〜6段階のパイプライン)

動画の説明は正確です。標準的なコンパイラ(GCC, Clangなど)は以下の流れです(若干の補完を加えて):

  1. 字句解析(Lexical Analysis):ソースをトークン(単語)に分解。
  2. 構文解析(Syntax Analysis):文法チェック → AST(抽象構文木)生成。
  3. 意味解析(Semantic Analysis):型チェック、変数宣言確認など。
  4. 中間コード生成(Intermediate Representation, IR):多くのコンパイラでここに段階が入る。
  5. 最適化(Optimization):ここが最も「職人技」。
  6. コード生成(Code Generation):対象CPU向け機械語出力。

最適化の変態的テクニック(追加例)

  • 定数畳み込み:3 + 5 → 8(コンパイル時計算)。
  • デッドコード除去:到達不可能なコードを削除。
  • ループ不変式移動:ループ内で不変の計算を外に出す。
  • インライン展開:関数呼び出しオーバーヘッドを排除。
  • その他:共通部分式除去(CSE)、レジスタ色付け(Graph Coloringによる割り当て)、ベクトル化(SIMD命令活用)など。

これらの最適化はグラフ理論やデータフロー解析に基づいています。

4. 面白い業界雑学・裏話

鶏と卵の問題:セルフホスティングとブートストラップ CコンパイラがCで書かれているのは有名ですが、最初はB言語(Ken Thompson)で書かれました。その後Cで書き直し、セルフホスティングを実現。

現代でもGCCなどは3-stage bootstrapを行い、信頼性を検証します。Zig言語などは最初からセルフホスティングを重視して開発されています。

LLVMの革命(2000年代〜) Chris LattnerらがUniversity of Illinoisで開始(Appleが強く支援)。

  • 中間表現(IR)の威力:言語(フロントエンド)はIRまで変換すれば、LLVMの強力な最適化・バックエンドでx86/ARM/RISC-Vなど複数アーキテクチャに対応可能。
  • これによりRust、Swift、Julia、Clang(C/C++)などが爆発的に発展。現代コンパイラ開発の「共通基盤」となっています。

JIT(Just-In-Time)コンパイラの衝撃 コンパイラ(事前)とインタープリタ(逐次)のハイブリッド。実行中に「ホットスポット」を検知して最適化コンパイル。

  • V8エンジン(Google、2008年〜):JavaScriptを爆速化。Ignition(インタープリタ)→ TurboFan(最適化JIT)などの多段パイプライン。
  • これでGoogle MapsなどのリッチWebアプリが現実的になりました。現代のJava(HotSpot)、.NETなどもJITを活用。

その他の雑学

  • コンパイラ戦争:昔はGCC vs 商用コンパイラ。今はClang/LLVMがGCCをシェアで追い抜く場面も。
  • バグの歴史:コンパイラ自身のバグは非常に危険(例: 1980年代の有名な「Ken Thompsonのトラストイングコンパイラ」バックドア話)。
  • 現代のトレンド:AOT(Ahead-Of-Time)コンパイル(モバイルアプリ)、WebAssembly(ブラウザ上ネイティブ実行)、MLIR(LLVMの次世代)など。

まとめ:抽象化の勝利

コンパイラの歴史は、「人間が機械の細部に囚われず、問題の本質に集中できるようにする」抽象化の歴史です。70年以上の蓄積(型理論、グラフ理論、最適化アルゴリズム)により、今やコンパイラは人間の手書きを凌駕するコードを生成します。

動画でも触れられているように、Fortran時代の「機械が作ったコードなんか信用できない」という声は完全に過去のものとなりました。Rustの所有権システムや、LLVMのモジュール性など、現代の言語設計はすべてこの基盤の上に成り立っています。

この分野に興味が出たら、実際にLLVMで簡単な言語を作るや、Tiny C Compiler(TCC)を読むのがおすすめです。コンパイラ開発は「プログラミングのプログラミング」と言える、非常に知的で楽しい領域ですよ!

(参考:動画URLを基に、歴史的事実は一般的な資料で補完しています)

コンパイラの歴史と仕組み:詳しい解説

この動画の内容を整理して、より詳しく説明します。


1. なぜコンパイラが必要だったか

アセンブリ言語の限界

1950年代、プログラムの規模が10万行を超えるようになると、アセンブリ言語(機械語に近い低レベル言語)では以下の問題が爆発的に大きくなりました。

問題 具体例
ループを書くのが大変 レジスタに回数を入れ、1回ごとにデクリメントし、0になったらジャンプ、という命令を全部手書き
変数管理が番地番号 メモリ4002番地が何のデータか覚えきれない
機種依存 IBM 704向けのコードはUNIVACでは動かない。機種変更のたびにゼロから書き直し
デバッグが地獄 レジスタと全メモリアドレスの値を紙に書き出し、CPUの動作を人間が頭の中で1ステップずつ再現
チーム開発が困難 担当者が退職したらそのコードを誰も読めない

→「人間の言葉に近い形で書けて、どんなCPUでも動く言語」が必要になった


2. コンパイラの誕生

グレース・ホッパー(1952年)

  • Aシステムを開発。事前に用意されたサブルーチンを組み合わせてプログラムを生成する仕組み
  • 「コンパイラ」という言葉を最初に使った人物
  • ただし現代的な「任意のソースコードを機械語に変換する」コンパイラとは少し異なる(ローダーに近いという評価もある)

フォートラン(1957年):最初の本格的コンパイラ

  • ジョン・バッカス率いるIBMチームが約3年かけて開発
  • 数式をそのまま書ける(FORMULA TRANSLATION
  • 当初、社内からも「そんなものが作れるわけない」と懐疑的な目で見られていた
  • 最大の技術的挑戦:ループの最適化
    • 科学計算には行列計算など大量のループが出てくる
    • データの依存関係を解析してレジスタ再利用を最大化することで、手書きアセンブリと同等の速度を実現
    • これは現代のスーパーコンピュータにも使われている依存解析の原型

⚠️ なお、ショートコード(1949年)、オートコード(1952年)など先行例もある。ただし業界への影響力という意味ではフォートランが別格。


3. コンパイラの内部構造

コンパイラは「工場のライン」のように複数の工程を経てソースコードを機械語に変換します。

ソースコード
    ↓
[字句解析]  → トークンに分割
    ↓
[構文解析]  → 構文木(AST)を構築
    ↓
[意味解析]  → 型チェック・スコープ確認
    ↓
[最適化]    → 無駄を取り除き高速化
    ↓
[コード生成] → 機械語命令を出力
    ↓
機械語(実行ファイル)

① 字句解析(Lexical Analysis)

ソースコードの文字列をトークン(意味のある最小単位)に分割します。

x = a + b
↓
[x] [=] [a] [+] [b]  ← 5つのトークン
  • ++のような2文字の記号は「先読み」で判定
  • スペースや改行は読み飛ばす

② 構文解析(Parsing)

トークンが文法的に正しいかチェックし、**抽象構文木(AST)**を構築します。

a + b * 6 の場合:
        [+]
       /   \
     [a]   [*]
           / \
         [b] [6]

掛け算が先に計算される(優先順位が高い)ため、*+の子ノードになります。これによりプログラムの構造が木として表現されます。

③ 意味解析(Semantic Analysis)

文法的には正しくても、意味がおかしいケースを検出します。

  • 整数と文字列を足そうとしていないか(型の不整合)
  • 宣言していない変数を使っていないか
  • 変数のスコープは正しいか

④ 最適化(Optimization)

ここがコンパイラの「職人技」の見せ場です。

最適化手法 内容
定数畳み込み 3+5 → コンパイル時に8に置き換える
デッドコード除去 絶対に実行されないコードを削除
ループ不変式移動 ループ内で毎回同じ値を計算していたら、ループの外に出す
共通部分式除去 同じ計算式が複数の場所に出てきたら、1度だけ計算して再利用
インライン展開 小さな関数をその呼び出し元に直接埋め込む(関数呼び出しのオーバーヘッドを消す)
末尾呼び出し最適化 末尾再帰をループに変換してスタックオーバーフローを防ぐ

最適化は連鎖することが重要です。例えばインライン展開でコードが展開されると、今度は定数畳み込みが適用できる場所が増える、という具合に、最適化が次の最適化を可能にします。

⑤ コード生成(Code Generation)

中間表現(IR: Intermediate Representation)

特定のCPUに依存しない中間形式。これを挟むことで:

C言語      → IR → x86用機械語
Rust       → IR → ARM用機械語
Swift      → IR → RISC-V用機械語

フロントエンドとバックエンドを分離することで、新しい言語を作ってもIR変換だけ作ればすでにあるバックエンドを再利用できる。新しいCPUが出てもバックエンドだけ追加すれば既存の全言語が動く、という強力な設計になっています。

SSA形式(Static Single Assignment)

現代のコンパイラがIRで使う特別な表現。変数への代入を必ず1回だけにするルール。

// 通常
x = 1
x = x + 2  ← 同じxに再代入

// SSA形式
x1 = 1
x2 = x1 + 2  ← 代入のたびに新しい名前

これにより「この値はここでしか作られない」とわかり、最適化を安全に適用しやすくなります。

レジスタ割り当て

グラフ彩色問題として解きます。

  • 2つの変数が同じタイミングで使われていれば「干渉している」
  • 干渉している変数は同じレジスタに入れられない
  • このルールをグラフで表し、隣接するノード(干渉する変数)に異なる色(レジスタ)を割り当てる

理論上はNP困難(変数が増えると解の候補が爆発的に増える)だが、ヒューリスティック(完璧な正解ではなく十分に良い答えを素早く見つける工夫)で実用的に解けます。


4. 高級言語の進化の歴史

言語の系譜

1952  Aシステム(ホッパー)── コンパイラという概念の先駆け
1957  FORTRAN ────────────── 科学技術計算。業界初の本格普及コンパイラ
1958  LISP ────────────────── プログラム=データ。GCを世界初実装
1958  ALGOL ───────────────── 構造化プログラミングの土台。商業的には広まらず
1959  COBOL ───────────────── 事務処理向け。今も銀行・行政の基幹システムで現役
1967  Simula 67 ───────────── オブジェクト・クラス・継承を世界初実装。OOPの発祥
1970  Pascal ──────────────── 教育向け。Pコード(仮想マシン用バイトコード)を実装
1972  C ───────────────────── ポインタ演算。OS・組み込み向け。移植性と低レベル制御の両立
1979  C with Classes ──────── ストラウストラップがCにオブジェクト指向を持ち込む → C++
1987  GCC公開 ─────────────── コンパイラが共有財産に
1990  Haskell ─────────────── 型推論・純粋関数型

特に重要なポイント

LISP(1958年)

  • コードとデータが同じ形(コードがコードを生成できる「マクロ」)
  • ガベージコレクション(GC)を世界で初めて実装
  • JavaのGCはここが起源

C言語(1972年)

  • UNIXを書き直すために作られた
  • ポインタ演算でメモリを直接操作できる
  • セルフホスティング:最初BはB言語で書かれたCコンパイラが、Cの成熟とともにC自身で書き直された。「言語が自分自身で自分を作れる」状態の達成

型推論(1970年代)

  • ミルナーらによるヒンドリー・ミルナー型推論
  • 型の制約を連立方程式のように立てて解く
  • RustやHaskellで「型を書かなくても動く」のはこれのおかげ

5. 現代のコンパイラ技術

LLVM(2003年〜)

クリス・ラットナーが大学院の研究として立ち上げ。

  • 汎用IRの仕組みを誰でも使えるようにした
  • 新しい言語を作る際、フロントエンドだけ作ればLLVMのバックエンドを再利用できる
  • Clang、Rust、Swift、AppleのコンパイラはすべてLLVMベース
  • LLVMの登場後、新しい言語を作るコストが劇的に下がり、2010年代に多くの新言語が誕生

JIT(ジャスト・イン・タイム)コンパイル

実行しながら、よく使われる部分だけをその場で機械語にコンパイルする技術。

仕組みの流れ:

  1. プロファイリング:どの関数が何回呼ばれたか、どのループが重いか監視
  2. ホットスポット特定:処理が集中する箇所を見つける
  3. その部分だけを積極的に最適化・機械語化

投機的最適化(Speculative Optimization): 「この変数はおそらくずっと整数だろう」と仮定して整数専用の速いコードを生成。仮定が外れたら**脱最適化(Deoptimization)**で汎用コードに切り戻す。

JavaのJVMは段階的JITを採用:

  • C1コンパイラ:起動時の軽い最適化(速く起動するため)
  • C2コンパイラ:頻繁に使われる箇所への重い最適化(ピーク性能のため)

**V8(Google Chromeのエンジン)**がこの技術でJavaScriptを劇的に高速化。Googleマップのようなブラウザアプリが快適になったのはこれが大きな理由です。

WebAssembly

  • ブラウザで動く低レベルなバイナリ形式
  • C++やRustのコードをコンパイルしてブラウザ上で高速実行
  • DOM操作はJavaScriptが得意、数値計算・画像処理はWebAssemblyが有利、という使い分け
  • ゲームエンジンや動画編集ツールをブラウザで動かす技術の鍵

6. 抽象化という思想

コンパイラの本質は抽象化にあります。

抽象化のレベルと言語

低 ← 抽象化レベル → 高
アセンブリ → C → C++ → Java → Python
(制御大・速度大)    (制御小・速度小・生産性大)

メモリ管理の例で見るトレードオフ

言語 管理方法 メリット デメリット
C 手動(malloc/free) 完全制御、高速 解放忘れでメモリリーク
Java/Python ガベージコレクション 楽、安全 GCで時々停止、リアルタイム性に問題
Rust 所有権システム(コンパイル時検証) GCなしで安全 コンパイル時間増加、学習コスト高

Rustの「ゼロコスト抽象化」

  • 使わない機能のコストは払わない
  • 使う機能は手書きコードと同等以上
  • コンパイル時にメモリの安全性を検証することで、実行時コストをゼロにする

7. コンパイラ vs インタープリタ

コンパイラ インタープリタ
実行方法 事前に全部翻訳してから実行 コードを1行ずつ読みながら即座に実行
起動 速い 遅い(逐次解釈)
開発サイクル コンパイル時間が必要 書いてすぐ結果が出る
最適化 事前に徹底的に可能 難しい
代表 C、C++、Rust Python(CPython)、Ruby

CPython(標準Pythonインタープリタ)の問題

  • GIL(グローバルインタープリタロック):複数スレッドがあっても同時に実行できるのは1スレッドだけ
  • マルチコアCPUをフル活用できない
  • 理由:参照カウント方式のGCをスレッドセーフにするための設計上の選択

PyPy:JITを使ったPythonの別実装。標準Pythonより数倍速い場合がある。

現代の言語実装は「コンパイラかインタープリタか」ではなく、いいとこ取りのハイブリッドが主流です(例:JavaのバイトコードコンパイルとJITの組み合わせ)。


まとめ

コンパイラとは「人間がCPUのことを考えなくていいようにするため」に生まれた技術

ホッパーやバッカスたちが70年以上前に「翻訳を機械にやらせれば人間は問題の本質だけ考えられる」と気づいたことが、今日のWeb・スマホアプリ・AIすべての土台になっています。

コンパイラの歴史は一言で言うと、「抽象化と実装コストのトレードオフをどこで引くか」という問いへの答えの積み重ねです。その答えは今も更新され続けています。