何故オブジェクト指向はクソなのか by Joe Armstrong(Erlangの作者)
このドキュメントは http://www.bluetail.com/~joe/vol1/v1_oo.html で行われたオリジナルの講演のコピーである
私が初めてオブジェクティブ志向プログラミングのアイデアを紹介された時、なぜか疑い深い気分になった。-ただ「間違っている」と感じたのだ。OOPが紹介されると、それらはとても広まり(なぜかは後に述べる)、OOPは「教会での宣誓」かのように評されるようになった。オブジェクト指向的であることは何やらすべての優れたプログラミング言語が持っていなければならないものとなったのだ。
Erlangが有名になると我々はよくこう質問を受けた。「Erlangはオブジェクト指向ですか?」。-そう、勿論本当の答えは「全く違います」だ。-が、それを大声で言いたくはなかった-。そこで我々は素晴らしくうまい回答を発明した。それを使えばこんな印象が与えられる。Erlangは(両手を精一杯広げれば)オブジェクト指向言語(の一種)といえるが、(われわれが実際言っていることを聞き、注意深く内容を精査すれば分かるが)本来はそうではない。
ここで、私はパリで行われた第7回IEEE論理プログラミング会議で行われた、当時のIBMフランスの社長のキーノートが思い出される。IBM Prologはそのとき大量のオブジェクト指向拡張の追加が行われたのだが、その理由について尋ねられた際に、かれはこう返した。
顧客がオブジェクト指向Prologを望んだ。だから我々はオブジェクト指向Prologを作ったのだ。
私はその時、「なんという単純さだ。良心の呵責も内省も、「コレを行うのが本当正しいことなのか」という問すらもない。。。」
私の主な反論は、OOPの基本的なアイデアに帰結できる。それらのうち幾つかのアウトラインを示し、それに対する私の反論を述べよう。
オブジェクトは関数とデータ構造を個別のユニットに相互に関連付けを行う。私の考えでは、これこそが基本的な間違いだ。なぜなら関数とデータ構造は全く異なる世界に属しているからだ。どうしてか?
関数はなにか仕事を行う。関数には出力と入力がある。入力と出力はデータ構造であり、それらは関数により変化させられる。ほとんどの言語では、関数は手続きシーケンスにより組み立てられる(これをして、その後にあれをして・・・というように)。関数を理解するためには物事がどのような順番で行われていくのかを理解する必要がある(遅延評価型の関数型言語や論理プログラミング言語ではこの制限は緩められる)
データ構造はただそれそのものだ。それ自体はなにもしないし、本来宣言的なものだ。データ構造を「理解する」ことは関数を「理解する」のに比べて非常に簡単だ。
関数は、ある入力を変化させ出力するブラックボックスとして理解される。入力と出力を理解すれば、それで関数を理解できたことになる。これはその関数の中身を書ける、ということを意味しない。
関数は、T1型のデータをT2型に変換する計算システム中の事象として観察されることにより、通常は理解される。
このように関数とデータ型は本質的に全く異なる生き物であり、同じ折に閉じ込めることは本質的に間違っているのである。
「時間」を例に取ろう。オブジェクト指向言語では、「時間」はオブジェクトだ。しかし非オブジェクト指向言語では、「時間」はデータ型のインスタンスにすぎない。例えば、Erlangには様々な種類の時間型があり、明確で一義的に型宣言により次のように規定される。:
- deftype day() = 1..31. - deftype month() = 1..12. - deftype year() = int(). - deftype hour() = 1..24. - deftype minute() = 1..60. - deftype second() = 1..60. - deftype abstime() = {abstime, year(), month(), day(), hour(), min(), sec()}. - deftype hms() = {hms, hour(), min(), sec()}.
これらの定義は特定のオブジェクトへ依存しないことに注目して欲しい。これらは汎用的であるから、時間を表現するデータ型はシステム内のどんな関数からも操作できる。
これらに関連付けられたメソッドなどないのだ。
オブジェクト指向プログラミング言語では、データ型の宣言はオブジェクトと結びついている。だからひとつの場所で全ての型定義を見つけることはできない。ErlangやCでは1つのインクルードファイルやデータディクショナリに独自定義のデータ型を全て定義することができる。オブジェクト指向言語ではできない。- なぜならデータ型定義はあらゆる所に散らばってしまっているからだ。
良い例を示そう。私が汎用的なデータ構造を定義しようとしたとしよう。汎用的なデータ型とは、システム中の「あらゆる所で」現れるデータのことだ。
Lispプログラマーは昔からよく知っているように、少数の汎用データ型とそれを操作する多数の小さな関数群が、多くの汎用データ型とそれを操作する少数の関数群を定義するよりもよい。
汎用データ型とはリンク付きリストや配列・ハッシュテーブルや、あるいは日付やファイル名のようにもう少し発展したオブジェクトのことをを指す。
オブジェクト指向プログラミングでは、汎用データ型を定義するオブジェクトを選ばなくてはならず、他のすべてのオブジェクトはそのデータ構造を使いたければ、それが定義されたオブジェクトを継承するしかない。さて、「時間」オブジェクトを作ることを考えた場合、これはどこに、どのオブジェクトの中に属するべきなのだろうか・・・
状態は諸悪の根源である。特に副作用を持つ関数は避けるべきだ。
状態はプログラミング言語における状態は望まれないものであるが、実世界は状態にあふれている。私は自分の銀行口座が今どういう状態なのか非常に関心があるし、銀行からお金を引き出したりお金を預けたりすれば、銀行口座が正確に更新されることを期待している。
これらの現実世界に存在する状態に対して、これらを扱うのにどのような機能をプログラミング言語は提供すべきだろう?
オブジェクト指向言語では「状態をプログラマから隠蔽せよ」と言われる。状態は不可視でありアクセス関数の通してのみ見ることができる。
伝統的プログラミング言語(CやPascal)では、状態変数の可視状態は、その言語のスコープルールによりコントロールされる、といわれる。
純粋宣言型言語では、状態はないと言われる。
システムのグローバルな状態はすべての関数に入力され、すべての関数から出力される。Monad(関数型言語)やDCG(論理型言語)のようなメカニズムがプログラマから状態を隠すのに使われるため、「状態が問題にならないように」プログラムすることができ、それでいて必要なときには状態へのフルアクセスをすることができる。
「プログラマから状態を隠す」というOOPLの選択は、取りうる中では悪い方の選択であった。状態を公開し、その厄介さを最大限小さくする道を探ろうとするよりも、奴らは状態を遠くへ隠してしまった。
- 理由1:学習するのが簡単だと思われた
- 理由2:コードの再利用性を簡単にすると思われた
- 理由3:過剰に宣伝された
- 理由4:新たなソフトウェア産業を創出した
1と2を裏付ける証拠を私は見たことがない。これらの理由は技術の裏に隠れた原動力に思える。もしある言語技術が非常に出来が悪く、それ自身が創りだした問題を解くのに新たな産業を創出するのだとすれば、金を稼ぎたい輩からすれば良いアイデアとなるのは疑いのないことだろう。
これこそが正に、OOPの裏にある真の原動力なのだ。
Joeのその後の見解が面白いですね。
http://www.infoq.com/interviews/johnson-armstrong-oop
http://tokoma1.hatenablog.com/entry/2015/06/07/083514