世界を動かす技術を、日本語で。

ビッグOOPs: 35年間の誤りの解剖

概要

  • Better Software Conferenceでのプレゼン動画が公開
  • 歴史的背景を重視しつつ2時間以内に収めた内容
  • Smalltalkなど一部の詳細エピソードは割愛
  • 追加資料や未公開ドキュメントの共有も検討中
  • 参考となる4つの歴史資料を紹介

Better Software Conference プレゼン動画公開のお知らせ

  • Better Software Conference での自分のプレゼン動画が YouTube で公開
  • プレゼンの 全編視聴 が可能(リンク:YouTube動画
  • 2時間以内 に収めるため、歴史的な正確性を維持しつつも内容を厳選
  • 重要性が高くないエピソードや Smalltalkの開発経緯 などは割愛
  • プレゼン準備のため、 数百〜千ページ超 の資料を調査・整理

プレゼンに盛り込めなかった内容と今後の計画

  • プレゼン外にも 共有したいエピソードや資料 が多数存在
  • 今後、 ライブ配信 で未紹介ドキュメントを解説する案を検討中
  • 視聴者からの コメントやフィードバック を募集中
  • プレゼン内容に興味を持った方に向けて、 4つの歴史資料 を推奨

推奨する歴史資料4選

  • The Early History of Smalltalk

    • Alan Kayによる Smalltalk開発の回顧録
    • C++ の普及により割愛したSmalltalkの詳細な経緯を知るのに最適
    • 付録付きPDF を推奨(インターネット上には付録なしも多いため注意)
  • A History of C++

    • Bjarne Stroustrupによる C++の開発史
    • 書籍『The Design and Evolution of C++』の第1章に相当
    • 本格的にC++の歴史を学びたい場合は 書籍の購入 を推奨
  • The Development of the Simula Languages

    • Ole-Johan DahlとKristen Nygaardによる Simula IおよびSimula 67の開発記録
    • クラス仮想関数 が初めて登場した経緯を記述
  • Origins of the APT Language for Automatically Programmed Tools

    • Douglass T. Rossによる APT言語の開発史
    • 1956年秋の「plex」開発が、 高度に構造化されたデータによるプログラミング の最初期事例として紹介

構造化データとその歴史的意義

  • 「plex」以前の 構造体的な概念 の事例については調査に限界
  • データを単なる記録としてではなく、 プログラムの動作を支援する構造体 として扱う発想の初出に興味
  • もし「plex」より早い事例を知っている方は 情報提供を希望

プレゼンの運営方針

  • 運営側から時間制限はなかった が、2時間超のプレゼンは長すぎると自己判断
  • コンパクトかつ本質的な内容の提供を重視

ご意見・ご質問 はコメント欄まで。今後のライブ配信や追加資料公開にもご期待ください。

Hackerたちの意見

面白いね。プレゼンターは、クラス階層がドメインモデルに対応するのが好きじゃないみたい。彼は、これがOOPの重要な特徴だと思っているようで、Smalltalkの支持者たちの引用を持ち出してる。でも、Smalltalkの世界ですら、OOPが実際に何かについて合意できてなかったんだよね(Kayの発言とSmalltalk-76ffの実際のアーキテクチャを比べてみて)。Smalltalkが重要性を失ったのもあっという間だったし、これ以上言及する必要はないと思う。むしろ、IEEEみたいな信頼できる業界団体を見た方がいいよ。彼らは自分たちの基準やベストプラクティスを発表してるから。例えば、OOPのマイルストーン(https://ethw.org/Milestones:Object-Oriented_Programming,_196...)では、Simula 67が最初のOO言語とされていて、OOは「3つの主要な特徴の組み合わせ」と定義されてるんだ。1) データとコードのカプセル化 2) 継承と遅延バインディング 3) 動的オブジェクト生成。クラス階層がドメインモデルに対応するなんて言及はないよ。だから、プログラミングパラダイムと、それを実践で使う人たちのやり方を混同しない方がいいかもね。パラダイムの一番の支持者が、実際にそれを適用する人たちではないってことは、今でも変わらない事実だし。言うのに2.5時間もかからないよ。

彼は、OOPの創始者たちがこのクラスとドメインの対応をOOPを考える正しい方法として提示したことを示すために、豊富な一次資料の引用をしてるんだ。Bjarne Stroustrupはただのランダムな人じゃないよ。

実際に見たの? 彼はSmalltalkよりもSimulaやC++についてずっと多く話してるよ。元の情報源、クリステン・ニガードやビャーネ・ストロヴストルップに戻ってるし。Smalltalkに焦点を当てるのは変だと思うな、だってその話じゃないし。

面白いのは、このコメントを読んでから動画を見たら、トークの最初の15分がまさにこのコメントを否定するために使われてるってこと。ゾッとするね。

このプレゼンテーションは最近ここで話題になってたよ: https://news.ycombinator.com/item?id=44596554 [動画] (37件のコメント)このリンクは、Casey Muratoriによる記事で、さらに探求すべきトピックに関する補足資料が載ってる。 - Smalltalkの初期の歴史 - C++の歴史 - Simula言語の発展 - 自動プログラムツールのためのAPT言語の起源

ここでも: https://news.ycombinator.com/item?id=44603205

スクリプトやトランスクリプトってどこかにある?話す速度より10倍早く読める人たちのために。

YouTubeのほとんどの動画にはトランスクリプトがあって、通常は説明文の下にボタンがあるよ。

Whisperでトランスクリプトを作ってもらって、いくつかざっと見たけど、結局1.5倍速で話を見ちゃった。https://pastebin.com/EngTq9ZA タイムスタンプを残したいなら、それも貼れるよ。

この動画はOOPの歴史を深く掘り下げていて、そういうのに興味があるならとても面白いよ。

https://rentry.co/2sky3whm

そして、ドメイン駆動設計(DDD)の支持者たちがいるわけですが、彼らはF#のようなML系の言語を使って、プログラムで無効な状態を表現できないようにすることを目指しています。

F#におけるDDDについての良い本がありますよ。「Domain Modelling Made Functional」ってやつです。

これが「スペクトラムの反対側」ってどういうこと? https://geeklaunch.io/blog/make-invalid-states-unrepresentab... で説明されているTypestateパターン(特にRustでよく使われる一般化されたバリエーション)は、まさに「ドメインモデルに合ったコンパイル時のカプセル化の階層」だよ。これはCasey Muratoriが言ってることの表現だね。文字通り、トレンチコートを着た継承ベースのOOPだ。

これは素晴らしいトークだね。OOPの歴史を1963年から1998年まで深く掘り下げてる。1998年に商業ゲーム「Thief」がエンティティコンポーネントシステム(ECS)アーキテクチャを使って開発されたっていうのがポイントで、普通のOOPじゃなかったんだ。これが彼が知っている現代の商業プログラミングにおける最も早い例だよ。OOPの歴史を調べている中で、ECSは1963年には存在していたけど、ほとんど忘れ去られていて、OOPが新しい言語に取り入れられたり、未来のプログラマーに教えられたりする際にソフトウェア設計の概念や方法論として持ち込まれなかったことを発見したんだ。これが起こった理由はいろいろあって、彼の長いトークではその歴史や重要な人物について説明している。

この要約ありがとう。私は趣味でプログラミングをしているけど、これを見たいと思ってる。いくつかの概念を知っておくと、文脈が理解しやすくなるんだ。

ECSはどんなプログラミングパラダイムでもできるよ。OOPと全然相性が悪いわけじゃないし。オブジェクトモデルがドメインの静的表現に一致する必要はない。業務アプリでは、ワークフローや処理に一致する方がずっと良い。実際、私はこの理由でRailsでもECSをやったことがある。JavaやC++のOOPの歪曲は受け入れなかったし、Erlangが最もOOP的な言語だと思ってる。カプセル化とメッセージパッシングがとても自然だから。

ECSはOOPの一部だから、Objective-Cみたいにプロトコルを導入した言語もあれば、C++やEiffelのように多重継承の道を選んだ言語もあるんだ。Smalltalkも、Smalltalk-80以降の実装では、単一継承モデルに加えてトレイトが追加されたんだよね。

失礼だけど、これってGPTが生成した要約みたいに読めるね。

ケイシーが大好きだし、このトークも大好き。アカデミアの外で深い研究をしている人たちを見るのはいつもいいね。これが私がこのテーマを理解してきたことを裏付けてくれる。彼がOOPの元の情報源について詳しく説明した後でも、人々がそれを見なかったり、彼の研究を無視してゴールポストを動かして「実際にはこれがOOPじゃない」と主張するのが面白い。業界が間違いに執着していることを認めたくないのか、ストックホルム症候群にかかっているのかもね。

彼の話は反OOPじゃないよ。特定のOOPの使い方に反対してるだけ。具体的には、ドメインモデルをコンパイル時の階層として表現することにね。彼自身がコードの中でOOPの概念を使っていることを強調しているし、OOP自体は間違いじゃない。多くの専門家が広めた主流の使い方が間違いだったんだ。

この話には掘り出し物がたくさんあるね。Q&Aの中に埋もれているこんな軽い一言でも: 「私はオブジェクト指向ではなく、動詞指向でコードを書くのが好きです。」… それは、どんなシステムを作っているかにも関係してるよね。人々がシステムにタイプを追加することが多いのか、アクションを追加することが多いのか。私は、アクションを追加することが多いと感じる。突然、なぜ一部の人や言語が doThing(X, Y) を好むのか、X.doThing(Y) を好むのかがわかったよ。

「表現問題」についての詳細はここで確認してね。 https://en.wikipedia.org/wiki/Expression_problem

でも、doThing(X, Y)を使うOOP言語もあるよ。例えば、AdaやJulia、Dylan、Common Lispなんかね。これも、プログラミングのパラダイムを一緒くたにしちゃダメな理由の一つだね。

なんで一部の人や言語がdoThing(X, Y)を好むのか突然わかった。ThingDoer.doThing(X, Y)を書き始めると、いろいろ疑問が出てくるんだよね。

bjarne(C++の創始者)がこのことについて言った名言があるよ。「統一関数呼び出し:x.f(y)とf(x,y)の表記上の違いは、操作に対して常に最も重要なオブジェクトが存在するという欠陥のあるOOの概念から来ている。これを採用したのは間違いだった。あの時は浅い理解だったけど(でもすごく流行ってた)。それでも、sqrt(2)やx+yをその見方から生じる問題の例として挙げたんだ。」https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p19...

開発者として25年間、OOPについて不満を言い続けてきたよ。C++やJavaをたくさん書いたけど、誰もその言語に関する私の専門性を否定できない。多くの人がそれを使って間違いを犯すのを見てきたし、特に無理に分類を当てはめるような状況で。で、同僚に自分の気持ちを愚痴ったら、疎外されて「スキルが足りない」と非難された。つまり、その時代のドグマが、皇帝に服がないと叫ぶカッサンドラたちを無視したんだ。Cやスクリプト言語のシンプルさは時代遅れだと見なされていた。ソフトウェア開発コミュニティは、Javaの問題がどれほど深刻だったかをようやく理解して、壁が崩れ落ちた。今では、非OOのPython(またはクラスを最小限に使うこと)の方が何よりも好きだ。言語の世界を巡って、LuaやClojure、たくさんのGroovyのプロジェクトをやった後、Functional Java、Kotlin、Golangに移った。

似たような経験がある。すごく難しいデバッグの問題が出たとき、チーム全体が私に頼ってきた。でも、OOPが良くないし、コードを複雑にしているって言うと、みんなに笑われた。どうして「頼られる」一方で、別のパラダイムを提案すると無視されるのか、全然理解できなかった。最近読んだ引用があるんだけど、要約すると「正統性は貧乏人の道徳的優越性の代替物」って感じ。

プログラミングではよくある現象だよね。最適じゃない解決策が長い間残っていることが多いのは、問題を解決しているからなんだ。権威ある人たちからXを教わると、それに頼りがちになるしね。別のことをするには経験とオープンマインドが必要だよ。GOTOの使い方もその一例。確かに、コードベースにはあまり入れたくないけど、それに対する過剰反応が、break文や複数のreturn文みたいな表現を言語から排除してしまうんだよね。

非OOのPythonなんて存在しないよ。この言語はSmalltalkみたいに、すべてがオブジェクトなんだ。普通の数値ですらね。ただ、オリジナルのPythonではそうじゃなかったけど、新しいスタイルのクラスがデフォルトの型システムになってからは、確かにすべてがオブジェクトになった。だから、Pythonみたいな言語を例にして反OOPの人たちに言いたいんだけど、Python 3.13.0(tags/v3.13.0:60403a5, 2024年10月7日、09:38:07) [MSC v.1941 64ビット (AMD64)] で「help」「copyright」「credits」または「license」と入力してみて。>>> x = 23 >>> type(x) >>> dir(x) ['abs', 'add', 'and', 'bool', 'ceil', 'class', 'delattr', 'dir', 'divmod', 'doc', 'eq', 'float', 'floor', 'floordiv', 'format', 'ge', 'getattribute', 'getnewargs', 'getstate', 'gt', 'hash', 'index', 'init', 'init_subclass', 'int', 'invert', 'le', 'lshift', 'lt', 'mod', 'mul', 'ne', 'neg', 'new', 'or', 'pos', 'pow', 'radd', 'rand', 'rdivmod', 'reduce', 'reduce_ex', 'repr', 'rfloordiv', 'rlshift', 'rmod', 'rmul', 'ror', 'round', 'rpow', 'rrshift', 'rshift', 'rsub', 'rtruediv', 'rxor', 'setattr', 'sizeof', 'str', 'sub', 'subclasshook', 'truediv', 'trunc', 'xor', 'as_integer_ratio', 'bit_count', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'is_integer', 'numerator', 'real', 'to_bytes'] >>>

彼の考えが全然理解できない。もし基底クラスへのポインタがあれば、異なる実装はポリモーフィックで、呼び出し元からは隠されている。それが全体のポイントで、ライブラリに基底クラスを持つエンジンがあれば、異なる人たちがそれを派生させてそのエンジンを使えるってことだ。彼のOOの定義は、私たちが慣れ親しんできたものとは違うと思う。もしかしたら、彼の定義には別の名前が必要かも。

あなたの考えは何? 自分のオブジェクトシステムを作ったら、それは確かにポリモーフィックだよね。今、自分のアプリケーションで世界をモデル化する必要を感じてる?

彼のOOの定義は、私たちが慣れ親しんできたものとは違うと思う。 いや、彼の定義は、まさに人々がOOPだと教えられていることそのものだよ。私が教わったこと、見てきたこと、OOPをやってると言う人たちが意味していることも、全部それだ。 もしかしたら、彼の定義には別の名前が必要かも。 いや、君の定義が別の名前が必要だよ。ポリモーフィック関数はOOPじゃないから。標準的なJuliaコードを誰かに見せたら、ポリモーフィック関数に基づいた言語だから、いろんなことだと言われるけど、OOPとは呼ばれないよ。重要なのは、ポリモーフィック関数はクラス階層なしでも動くってこと。クラス階層がないものを「OOP」と呼ぶのは頭おかしいよ。

彼のOOの定義は、私たちが慣れ親しんできたものとは違うと思う。もしかしたら、彼の定義には別の名前が必要かも。 「OOP」が異なる意味で使われるのを見たことがある。例えば、言語について言われることもあれば、言語機能とは無関係で、単にコードベースの「スタイル」やデザイン/アーキテクチャ/組織について言われることもある(ある人たちは、Cのコードベースが「オブジェクト指向」と呼ばれることがあるけど、通常はvtableや関数ポインタを使っているからだ)。 「OOPをプログラミング言語の記述子として話すとき」でも、異なる意味で使われるのを見たことがある。例えば、多くの人がRustはオブジェクト指向ではないと言う。でも、Rustはデータ型を定義できて、データ型にメソッドを定義できるし、インターフェースと呼べるものに基づいてポインタ+vtable構造を作るための言語機能もある(Rustの「トレイト」)。 「唯一」欠けているのは、エルゴノミクスや継承、あるいはOOPの文化かもしれない。だから、「OOP」の一つの定義は「継承を言語機能として持つプログラミング言語」になるかもしれない。でも、そう考えない人もいるし、プログラミング言語の記述子として使うときでもそうだ。彼らは、実際にはメッセージパッシングやカプセル化、あるいはその組み合わせについてだと思っているかもしれない。 「スタイル」やデザインについて話すときも、異なる意味を持つことがある。この投稿が言及しているトークでは、スピーカーが「ドメインモデルに合ったカプセル化のコンパイル時階層」について言っている。大学の教授がOOPを「現実世界」をモデル化する方法として教えているのを見たことがあるし、継承は意味的な「is-a」関係であるべきだと言っていた。 私は、トークがそういうことについてだと思う。でも、上で言ったように、OOPのコードベースはドメインモデルを表すコンパイル時階層である必要はないと考える人もいる。ポリモーフィズムのメカニズムやコード再利用の方法として使われることもある。 とにかく、私が言いたいのは、「OOP」が抽象的に何を意味するかについての具体的な議論はあまり有益ではないと思うし、この特定の文章では著者が自分の意味を明確にするために時間をかけているので、それに従った方がいいと思う。

OOPにはたくさんの欠点があって、全ての文脈で良い選択とは言えないけど、最近の普遍的な嫌悪感が理解できない。OOPの技術は、データが長時間実行されるプロセスのメモリにある場合に最も意味があったと思う。初期のMS Officeとかを考えてみて。 それ以降、ディスクに書き込まれていないものは一時的なものと見なすべきコンピューティング環境に変わった。UIはウェブベースで、スレッドやプロセスだけでなく、ユーザーのアクション間で全機械を行き来することもある。プロセスはいつでも終了して再起動されると考えるべきだし、などなど。 だから、今日では複雑なオブジェクトグラフをメモリに保持する意味がかなり減った。実際のオブジェクトグラフは永続ストレージに表現されるべきで、プロセス内のロジックは数学的な関数のように動く。フロントエンドの表現(HTML、JSONなど)とストレージの表現(フラットファイル、データベースなど)を行き来する。 「ビジネスロジック」はその関数のサブクローズに過ぎない。そういう環境では、関数型やCスタイルの命令型プログラミングがより適しているのは明らかだ。入力から複雑なオブジェクトグラフをインスタンス化して、一度トラバースして、また破棄するなんて意味がないし、それをユーザーのインタラクションごとに繰り返すのも無駄だ。でも、それが突然常に悪いパラダイムだったわけじゃない。環境が変わっただけだよ。 それに、まだ意味がある他の文脈もあるかもしれない。例えば、高レベルのスクリプトやゲームプログラミングとか。

そのトークが何についてだったか、3分でも理解しようとしたの?

ちょっと混乱してるんだけど。これがトークの中心的な主張とどう関係してるの?(「ドメインモデルに合ったカプセル化のコンパイル時階層は間違いだった」) OOPが最近では少し薄まった用語で、異なる人や文脈、コミュニティで異なる意味を持つことは理解してるけど、著者は自分が何を話しているのかを十分に詳しく説明していたよ。