Elixir から Elm の流れで、いよいよオブジェクト指向に対する懐疑心が無視できないレベルに達した2017年冬。

このエントリは Elm2 Advent Calendar 2017、2日目の記事になります。


Disclaimer: 勢いで書いてしまった後に改めて読み返してみると、Elmの中身には全く触れてないような気もしなくはない感じになってました… その辺を期待している方はブラウザ(のタブ)をそっ閉じして明日の記事にご期待下さい。


東京都港区の会社でインフラの仕事をしているフリをしながら、Elixir や Elm での関数型プログラミングに四苦八苦しつつ、Cotoami というよく分からないアプリケーションを作ったりしています。

今回は、まだ駆け出しの関数型プログラマーである筆者が、関数型プログラミングの洗礼を受けたことによって、長年慣れ親しんできたオブジェクト指向に対する見方が変わってきたという話について書いてみたいと思います。たとえて言えば、外国語を勉強することで、初めて日本語というものを客観的に見る機会を得たという体験に似ているかもしれません。

90年代からゼロ年代の中盤ぐらいまでにオブジェクト指向でプログラミングを始めた人間にとって、その考え方はプログラムデザインの共通言語のようになっていて、それ自体を疑うということには、なかなかなりづらい状況が長く続きました。Paul GrahamJeff AtwoodLinus Torvalds のような著名な人たちがオブジェクト指向に対する批判や懐疑を表明しても、「使い方の問題だよね」という感じで、オブジェクト指向そのものの問題ではないというのが多くの支持者の反応だったように思います。

あの TDD(Test-Driven Development)に対しても「Faith-based TDD」として同じ構造の批判がなされています(参考:「TDD再考 (8) – 凝集性(cohesion)とは何なのか?」)。このような議論の際によく見られる「◯◯が機能しないのは、◯◯のやり方を間違えているからだ」のような論法は、「No True Scotsman fallacy(本当のスコットランド人なら◯◯などしない論法)」だとの指摘もあります。

そもそもオブジェクト指向への批判が、関数型プログラミング界隈から行われることが多かったということもあり、その筋の人たちにとっては自明のことでも、オブジェクト指向しか知らない人たちにとっては、その指摘自体をうまく理解できないという非対称な構造がありました。

Information hiding vs. Explicitness

そのような状況の中で、オブジェクト指向への敬虔な信仰を残したまま、関数型プログラミングの門を叩いたわけですが、そこでいきなりオブジェクト指向の中心的な価値を全否定されるという事件が起こります。

オブジェクト指向では、状態というものがインタフェースの向こう側にあって、どのように実現されているか見えないようになっており、それがカプセル化、あるいは情報隠蔽と呼ばれる、複雑さを扱う技術の核心になっています。

ところが、関数型プログラミングでは状態の遷移を隠さずに、関数の入出力として表現しようとします(状態の遷移が入出力で完結している時、この関数を「純粋な関数」と呼ぶ)。

状態遷移が関数の入出力に限定されている時、プログラムの動作を把握するのは飛躍的に楽になります。一方で、オブジェクト指向ではプログラム上は簡潔に見えても、水面下に沢山の状態が隠されているので、何か問題が起きた時に状況を把握するのは容易ではありません。


The problem with object-oriented languages is they’ve got all this implicit environment that they carry around with them. You wanted a banana but what you got was a gorilla holding the banana and the entire jungle. — Joe Armstrong(関数型プログラミング言語 Erlang の作者)

「欲しかったのはバナナだけなのに、それを持ってたゴリラどころか、ゴリラがいたジャングルごとついてきた」って、分かりやすくてなかなか面白い表現ですが、それを言ったら、関数型だってバナナだけじゃ済まないだろっていう話もあるような・・・


ところが、なぜオブジェクト指向が状態遷移を隠蔽していたかと言えば、インターフェースに対してプログラミングすることによって、プログラムが実現すべき要求だけを簡潔に表現できるということがあったと思います。そうせずに、単純に状態遷移を全て入出力で表現しようとすると、プログラムはとても読み辛いものになってしまいます。この関数型特有の問題に対応するため、いくつかの関数型言語ではモナドという「本来の計算とおまけを切り離す」ための仕組みが導入されており、これはオブジェクト指向でやっていた情報隠蔽が形を変えて現れたと言えるのかもしれません。

興味深いのは、オブジェクト指向と関数型で、状態の扱いに関して言えば、全く逆の考え方になっているところです。オブジェクト指向では複雑性を扱うための核心となっていた考え方が、関数型プログラミングでは悪として扱われている。

ここでぼんやり、オブジェクト指向と関数型のハイブリッド言語ってどうなのよ? という疑念が立ち上がってくることになります。

A paradigm is a mindset

そんな思いを抱いたまま、8月に開催された Elm Tokyo Meetup #4 に参加して、そこで教えて頂いた、Elmアプリケーションのプログラムデザインについての動画を見ている内に、オブジェクト指向と関数型プログラミングはそもそも両立しないのではないかという印象はさらに強くなっていきました。

この動画を見る限り、Elmのコミュニティには、オブジェクト指向からの編入組が結構いるのではないかという印象を受けます。というのも、そこで語られていたのは、コンポーネントという、オブジェクト指向的なデザインを導入したみたけれど、どうもうまく行かなかったので、一度その辺の考え方をリセットして、もっと原理的なところからプログラムデザインを考え直してみようという話だったからです。

動画の中で、オブジェクト指向と関数型というのは、プログラミング言語の問題というよりもマインドセットの問題なのだという話が紹介されています。つまり、言語のパラダイムとは関係なく、プログラマがオブジェクト指向のマインドセットでコーディングしていれば、たとえElmのような純粋関数型の言語であっても、オブジェクト指向的にデザインされてしまうということです。

そのように考えると、マルチパラダイムの言語では、良く言えば、プログラマのマインドセットによって多彩なプログラムデザインが実現できるということになるけれど、悪く言えば、互いに相容れない複数のマインドセットを想定している場合は、単に混乱の元になるだけではないかという感じがして来ます。

Prefer duplication over the wrong abstraction

そのモヤモヤ感が強まった中で、さらに追い討ちをかけて来たのが、Cindy Sridharan 氏による「小さな関数は有害だと考えられる」というタイトルの記事です。

この記事で彼女は、一般的には名著とされている「Clean Code」に書かれているような、オブジェクト指向時代に生まれた設計指針は、むしろ過剰な構造化を誘発して、可読性や柔軟性を欠いたコードになることが多いのではないかという、いかにもその筋で炎上しそうな指摘をしています。

この指摘の背景には「そもそもオブジェクト指向が想定する抽象化が容易ではない」という問題意識があります。抽象化が容易でないのに、オブジェクト指向の設計指針には、その分割が本当に必要だと確信できるより前に、プログラムの分割を進めさせてしまうような圧力があります。

SRP(Single Responsibility Principle)

SRPでは、「1つのクラスは1つの責務を持つ」を原則とします。複数の責務を 1つのクラスに持たせないこと。1つの分かりやすい役割をクラス分割の境界 とすること。1つのクラス内に入る要素(属性や操作)が、1つの目的に向かっ て凝集していること。これが原則です。

クラスに変更が起こる理由は、一つであるべき。
A class should have only one reason to change.

– ソフトウェア原則[3]

 

ISP(Interface Segregation Principle)

クライアントは自分が使わないメソッドに依存することを強制されない。
Clients should not forced to depend on methods that they don’t use.

クライアントが本当に必要としているインターフェイスのみ が、クライアントから見えるべきで、他のメソッドには依存したくない。依存 を最小にして、変更の伝播を最小限に食い止めたい。Segregationとは分割、分 離、という意味です。つまり、ISPは「インターフェイスをクライアント毎に分離しよう」という原則なのです。

– ソフトウェア原則[4]

この問題について、Elmの作者である Evan Czaplicki 氏も同じような話をしています。上の動画と同じ「Elm Europe 2017」にて行われた発表によれば、

JavaScript での開発では、モジュールを細かく分けて、小さなファイルを沢山作る傾向があるが(”Prefer shorter files.”)、何故そのようなことになるかと言えば、

1) 一つのモジュールが大きくなると、その内部で何か想定外のことが起こる可能性が高くなる(想定外の状態共有や変更)
2) Static type のない JavaScript では、リファクタリングのコストが高くなるので、早い段階で分割を進めてしまう

という理由があるからではないかと指摘しています。Elmでは、1) に対しては、副作用がない純粋関数型であること、2) に対しては、強力な型システムがあることによって、これらの懸念を払拭できるため、先走りのモジュール分割を避けることが出来るというわけです。実際に、Elmでは一つのファイルやモジュールが大きくなることについて、他の言語(特にオブジェクト指向言語)よりも寛容であるということがよく言われています。

そもそも適切な抽象化が難しいのに、その抽象化が有用であるという証拠が揃わない内に分割を進めてしまうと、より不適切な分割をしてしまう可能性が高くなってしまいます。先走って分割したモジュールが、後々の状況変化に対応できなくなって、例外条件に対応するコードが増えて行き、そしてスパゲッティ化していく過程というのはシステム開発の現場で働く多くの人が目撃しているのではないでしょうか。

duplication is far cheaper than the wrong abstraction, and thus to prefer duplication over the wrong abstraction

「間違った抽象化よりも、コードの重複の方を好む」ということで、長らく信奉されてきたDRY原則への挑戦がここでは行われています。

Leaky abstraction

そして、偶然なのか何なのか、これも同じ8月に、オブジェクト指向における抽象化がなぜ難しいかというのを良く表している大変興味深い記事を見つけることになります。

カプセル化することが自己目的化していて、何のためにカプセル化するのかという視点が極めて希薄なことです。その結果、「うまいカプセル化」「まずいカプセル化」の区別がない、という状況に陥っています。

本来は、値引き判定のロジックをどのオブジェクトに配するかを決めるにあたって、どのような知識を隠蔽すべきか、あるいは裏返して言えば、どのような知識は開示して構わないかという点に思いをめぐらすべきでした。

解決策は、「データとロジックを一体に」という、どちらかというとゲームのルールのような具体的で単純なルールから視点を引き上げ、「情報隠蔽(=知識隠蔽)」のような、より本質的な、目的志向的な設計原則に立ち帰って考えることです。

この記事の要旨は、増田亨氏の「現場で役立つシステム設計の原則」という書籍で紹介されているオブジェクト指向のコード例について、「データとロジックを一体に」というオブジェクト指向の表面的なルールに囚われ過ぎて「何を隠蔽して何を表に出すのかという設計判断」を蔑ろにしているという指摘です。

しかし、この記事を読んで個人的に思ったことは、設計判断の根拠となる「スコープが適切でない」ということを、後から文脈をズラすことでいくらでも言えてしまうというのが問題の本質じゃないか、そこに「データ・ロジック一体型設計」の限界があるということなのではないか、ということでした。

うまく抽象化したつもりでも、どこかに必ず漏れが出てきてしまうという話は、「Joel on Software」の「The Law of Leaky Abstractions」という、2002年に書かれた記事に出てきます。

TCPプロトコルが、下位のネットワークをうまく抽象化しているように見えて、実際はいくつかの例外ケースで、その隠蔽しているはずのネットワークの存在が漏れて出してしまう(Abstraction Leak)。そうなった時にかかるコストというのは、抽象化がなかった時よりも高くついてしまう可能性があります。隠蔽された部分の知識も結局のところは必要なのだとなれば、抽象化された部分と隠蔽された部分の両方の知識が必要になるからです。

Objects bind functions and data structures together

オブジェクト指向の問題点を指摘する場合、一番厄介なのは、オブジェクト指向に定まった定義がないという事実です。このブログでは以前、オブジェクト指向の歴史を遡って、あれってそもそも何だったのかということについて検討したことがあります。

歴史的な経緯から言えば、オブジェクト指向を発案したアラン・ケイ氏が言うところの「メッセージング」が、オブジェクト指向の本質だということなりそうですが、一般に普及した「オブジェクト指向言語」と呼ばれるもので、メッセージングをサポートしているものはほとんどありません。メッセージング、あるいはそれが実現する動的結合(late binding)だけを考えると、それは今、オブジェクト指向と呼ばれるものよりも遥かに広い範囲で利用されていますし、実際にはオブジェクト指向言語じゃなくても実現できることを考えると、C++ をきっかけに流行した「抽象データ型」を起源とする流れが、一般的に認識されているオブジェクト指向だと考えて差し支えなさそうです。

ちなみに、オブジェクト指向信者の反論を「No True Scotsman fallacy」だと指摘した Lawrence Krubner 氏によれば、一般的にオブジェクト指向の強みだと思われているほとんどの要素はオブジェクト指向固有ではなく、オブジェクト指向固有の強みなど、実際には一つもないそうです。

オブジェクト指向固有でないものを除外していくと、最後に残るのが「データとロジックを一体に」という先ほどのルールです。そして、どうもこのルールがオワコンになりつつあるのではないかというのが、この約1年間、Elmでプログラミングをしていて実感するようになったことです。

関数型プログラミングをしていると、データとロジックが分かれていることのメリットを実感する機会が度々あります。アプリケーションにはアプリケーション固有の複雑さというものがあって、それらは多くの場合、必要な複雑さである場合が多いような気がしています。オブジェクト指向では、データとロジックを一緒にしなければならないという制約のために、それらの例外的だと思われるケースを捨象して、現実に即さないモデルに(強引にでも)落とし込むことになります。必要な複雑性を無理に捨象しようとするから、Abstraction Leak が起こります。

オブジェクトに関数が結びついているからこそ「このメソッドはこのオブジェクト構造を処理するためのものだ(他の用途には使えない)」という風に専門化できていたんであって、データと関数を個別のものと扱う以上は、「この関数はこのオブジェクトだけを扱う」という前提を置けないのです(当たり前です)。あるオブジェクトと別のオブジェクトが、型であったりクラスであったりが異なったとしても、関数は、そのオブジェクトが、関数の処理できる構造であれば、処理できるべきなんです。関数とオブジェクトが独立しているというのはそういう意味であるべきです。であれば、すべての関数にまたがるような、共通の汎用データ構造があって、すべてのデータはその汎用性を担保してたほうがいい。 Clojureの世界観 – 紙箱

Oscar Nierstrasz 氏が、彼のオブジェクト指向批判の中で、「オブジェクト指向とは、つまりモデリングなのだ」と喝破していますが、複雑な事象を分かりやすい用語(ターム)の集合に落とし込めるという先入観が、アプリケーションレベルの複雑性を扱う時に明らかな障害となって現れるケースが多くなっているような気がします。

メッセージングが今のオブジェクト指向と関係ないのだとすれば、「データとロジックを一体に」がオブジェクト指向の核心になりますが、そうだとすれば、オブジェクト指向自体がオワコンだという結論になってしまいます。そして、それはどうもそうっぽいという感じがしているのです。

Solve problems of its own making

ここからは少し余談になりますが、以上のような気づきを得た上で、過去のオブジェクト指向批判の文章を読むといちいち首肯できることが多くて困ってしまいます。

オブジェクト指向というのは、オブジェクト指向にしかない問題を作り上げて、それを解決するためのツールを作るというマッチポンプ的なことをしてお金を稼いでいるという話があります。

If a language technology is so bad that it creates a new industry to solve problems of its own making then it must be a good idea for the guys who want to make money. Why OO Sucks by Joe Armstrong(さっきのバナナの人)

あるいは、オブジェクト指向ではそもそも過剰な複雑性を作り込んでしまう傾向があるという批判があります。何故かといえば、オブジェクト指向には、インターフェースに対してプログラミングするという考え方があるので、プログラムは自然にレイヤー構造になっていくからです。もう古典と言っても良いかもしれない、Martin Fowler 氏の「リファクタリング」にも、

「コンピュータサイエンスは、間接層(indirection)を設けることであらゆる問題が解決できるという信念に基づいた学問である。― Dennis DeBruler」

とあったりしますが、

しかし、間接層はもろ刃の剣であることに注意しなければなりません。1つのものを2つに分割するということは、それだけ管理しなければならない部分が増えるということなのです。また、オブジェクトが、他のオブジェクトに委譲を行って、その先もさらに委譲を繰り返すような場合、プログラムが読みにくくなるのも事実です。つまり間接層は、最小限に絞り込むべきなのです。

とは言え、実際には過剰なレイヤー構造になっていることが多いような気がします。アジャイルという考え方が出てきて、「Just enough」や「YAGNI」なんてことが言われるようになりましたが、今を思えば、これはオブジェクト指向の側にそもそも過剰な複雑性を生む性質があったために、わざわざ言わなければならなくなったことのようにも思えてきます。

オブジェクト指向特有の問題として指摘されている中で、ああこれはと思ったのは、オブジェクトをどう作るかという問題、すなわち Dependency Injection の問題です。

OOP was once seen as the silver bullet that was going to save the software industry. Nowadays we need a silver bullet to save us from OOP, and so, we are told (in the next paragraph), the Inversion of Control Container was invented. This is the new silver bullet that will save the old silver bullet. Object Oriented Programming is an expensive disaster which must end | Smash Company

これはまさにオブジェクト指向にしか存在し得ない問題を、比較的大掛かりに解決しようとした例の代表だと言えそうです。今改めて考えると、この枠組みには二つの問題があって、一つはインタフェースベースのポリモーフィズムの問題(あらかじめ想定したインタフェースの範囲の柔軟性しか持てない)、そしてもう一つは、本当に複数の実装を必要とするケースがどれだけあるのか? という問題です。後者は仮に統計が取れれば面白い数字が出てきそうですが、少なくともテスト時にモックオブジェクトに置き換えられるという主張は、テスト容易性はすなわち良いデザインではないと、Ruby on Rails の作者である David Heinemeier Hansson 氏に批判されています。

最後に

なんか、「もうやめて!オブジェクト指向のライフはゼロよ!」みたいな感じになってしまいましたが、これは完全に Elm のせいです。

組織やチームの話を追いかけると、プログラミングは基礎教養なんだって話に辿り着いてしまう件

まさにそんな感じの話がInfoQで紹介されていた。

オブジェクト指向とは何だったのか?

このブログでも何度か取り上げているように、プログラミングにおけるここ数年間のトレンドで最も大きなものは、関数型プログラミングの隆盛だと言って良いだろう。そのトレンドと共に相対的に存在感を失いつつあるのが、それまで長らく主流を占めていた「オブジェクト指向」という考え方である。関数型プログラミングの観点から眺めると、オブジェクト指向プログラミングではシステムの状態を暗黙に扱う(情報隠蔽)ために実行時の挙動が予測しづらくなり、高度な並行性が求められる現在の環境では、信頼性を確保する際の大きな障害となるように見える。

しかし、筆者も含めて、オブジェクト指向という考え方にあれだけ熱狂した立場から考えると、昨今のオブジェクト指向に対する評価に対して若干の違和感を感じざるを得ないのも事実である。というわけで今回は、オブジェクト指向の発案者による発言を参照しながら、改めて「そもそもオブジェクト指向とは何だったのか?」ということについて考えてみたい。

まず始めに「オブジェクト指向」という言葉の意味については、どこかに正式な定義が存在する、というわけではないようだ。これは「関数型」についても同様のようである。なので、このブログで書かれていることはある種の解釈や立場表明をしているに過ぎないということに注意して頂きたい。

メッセージング

「オブジェクト指向(object-oriented)」という言葉を最初に提唱したのは、Smalltalkというオブジェクト指向環境を発明したアラン・ケイ氏だと言われている(これは本人も認めている)。

When and where was the term “object-oriented” used first?

At Utah sometime after Nov 66 when, influenced by Sketchpad, Simula, the design for the ARPAnet, the Burroughs B5000, and my background in Biology and Mathematics, I thought of an architecture for programming. It was probably in 1967 when someone asked me what I was doing, and I said: “It’s object-oriented programming“. – Dr. Alan Kay on the Meaning of “Object-Oriented Programming” (強調は筆者)

そのアラン・ケイ氏によれば、オブジェクト指向で最も重要なのは「メッセージング」なのだと言う。

Smalltalk is not only NOT its syntax or the class library, it is not even about classes. I’m sorry that I long ago coined the term “objects” for this topic because it gets many people to focus on the lesser idea.

The big idea is “messaging” – that is what the kernal of Smalltalk/Squeak is all about – Alan Kay On Messaging (強調は筆者)

上記引用にもあるように、ケイ氏は「オブジェクト(指向)」という言葉を持ち出したことに後悔さえしていると言う。メッセージングの観点から言えば、クラスやオブジェクトさえも重要ではないということから、本来は「メッセージ指向」と呼ぶべきだったのかもしれない。

さて、このメッセージングであるが、オブジェクト指向の発案者によってその重要性が強調されているのにも関わらず、職業プログラミングの主流を占めていた C++ や Java でオブジェクト指向を実践してきた多くのプログラマにとっては、むしろ馴染みの薄いものではないだろうか。

ケイ氏は、オブジェクトを、ネットワークを形成してメッセージを送り合うコンピュータのメタファーとして捉えており、インターネット上のサーバーのように、リクエストをメッセージとして受け取り、そのメッセージをサーバー側で解釈して何らかの処理を行うというモデルを想定していた。オブジェクトへのリクエストという意味では、C++ や Java のメソッド呼び出しがメッセージングに相当するように思われるかもしれないが厳密には異なる。ケイ氏の開発した Smalltalk では、このメッセージを非同期にやり取りするという、今まさに広がりを見せつつある並行処理のモデルを70年代の段階で実現していた(後にアクターモデルと呼ばれる)。しかし、その Smalltalk においても、ある段階から現在主流になっている(メソッド呼び出しと同等な)同期的な呼び出しに移行し、その後も「メッセージ」のような用語が維持されたためにメッセージングにまつわる誤解と混乱が生じた、という経緯があったようだ(参考: Alan Kays Definition Of Object Oriented)。


[2016/05/09追記]
この部分に事実誤認があるとの指摘をコメント欄にて頂いた

「ケイ氏の開発した Smalltalk では、このメッセージを非同期にやり取り」については、

Versions of Smalltalk before Smalltalk-80 were still largely based on the (asynchronous, unidirectional) ActorsModel of computation, but with Smalltalk-80, the developers of SmalltalkLanguage switched entirely to the (synchronous, bidirectional) procedural model, while misleadingly retaining the ActorsModel terminology (such as “messages” for what essentially are procedure calls rather than one-way notifications). – Alan Kays Definition Of Object Oriented

この言及だけを参考にして書いてしまっていたのだが、

アラン・ケイらの Smalltalk-72 が大きな影響を与えてはいますが(https://www.cypherpunks.to/erights/history/actors/actor-induction.pdf の Acknowledgements 等)、アクターモデル自体はカール・ヒューイットの考案であり、Smalltalk や ケイのメッセージングとはまた別物です。念のため、ケイの関わった Smalltalk(-72、-74、-76、-78)は非同期のメッセージングベースで実装されたことはありません。 – コメント欄(sumimさん)より

とのこと。ご指摘感謝!

[2016/05/10追記]

さらに別の方からコメントを頂く。

Actor Model自体がCarl Hewittの考案であり、Kayのメッセージングとは「別物」ということですが、歴史はしばしばもうちょっとややこしいものです。私が見聞きしたか限りにおいては、KayがMITに行ってSmalltalk-72に関する講演を行った時に、実際の実装とは別に元々のアイディアとして「ネットワークで接続されたオブジェクト群がメッセージを交換しながら計算を行う」という話をし、その時に聴衆にいたHewittがActor Modelの着想を得た、という経緯もあります。Actorの当初の論文には謝辞としてAlan Kayの名前があるもの、それ以上のクレジットがなかったために、Carlが「コミュニティー」からもうちょっとアイディアの出典を正直に述べなくてはならないという批判を受けることになったという話もあります。 – コメント欄(よしきさん)より

Smalltalk に興味がわいた方は、sumim氏による Smalltalk-72 入門も是非ご参考あれ。


messaging

このメッセージングに関係して、最近タイムリーなブログ記事を見かけた。メッセージングを実現している環境として、実は Erlang/Elixir が最もアラン・ケイ氏のオブジェクト指向を体現している言語環境なのではないかという考察である。ちなみに、この二つの言語は一般的には関数型プログラミング言語として認識されているはずである。

この記事の中で紹介されているように、Erlang の開発者である Joe Armstrong 氏が「Erlang が最もオブジェクト指向に近い言語である」という趣旨の発言をしているようだ。

しかし、何故メッセージングが最も重要なのだろうか? それは、メッセージングという仕組みの内に、ケイ氏が考えるオブジェクト指向の要点が全て含まれているからである。ケイ氏は、メッセージングの位置づけを日本語の「間」という言葉で説明しているが、氏によれば、システムの「間」つまりコミュニケーションに着目することによって、システムにとって重大な目的(ゴール)だけを表現し、その他の詳細については二次的に決定すれば良い、そしてそのような決定の先送りがシステムの寿命を延ばす鍵になるのだと言う。メッセージがどのように解釈されるのか、クラスから作られたオブジェクトによって処理されるのか、そのクラスは継承によって構造を共有しているのか、あるいはプロトタイプベースなのかという問題は全て二次的な問題である。

出来るだけ多くのことを「work in progress」として扱うことによって進化してきた Smalltalk は、ある段階からあらゆることを決定し固定化したがる人たちによって不毛な議論が繰り返され、その結果、進歩が停滞しているとケイ氏は苛立ちを見せていた。この「work in progress」の考え方は、21世紀になってから普及したアジャイルの考え方そのものである。アジャイルはオブジェクト指向のコミュニティが生み出したものであるが、その考え方の源流はオブジェクト指向の始まりから既に埋め込まれていた。

抽象データ型

ここまでの話で、オブジェクト指向の発案者であるアラン・ケイ氏の、メッセージングという真意が明らかになった。しかし、これだけでは職業プログラマの多くが実践してきたオブジェクト指向らしきものは一体何だったのかという疑問が残る。

One of the things I should have mentioned is that there were two main paths that were catalysed by Simula. The early one (just by accident) was the bio/net non-data-procedure route that I took. The other one, which came a little later as an object of study was abstract data types, and this got much more play. – Dr. Alan Kay on the Meaning of “Object-Oriented Programming”

上記引用のように、ケイ氏は、オブジェクト指向の起源となった Simula という言語からは、ケイ氏の主張する「the bio/net non-data-procedure route」の他に、「抽象データ型」を中心に据える流れも起こり、こちらの方がむしろその後主流になったと説明している。抽象データ型の流れは、C++ や Java に引き継がれ、最近では Scala など、いわゆる静的型付け(static typing)として職業プログラマの間では主流になった。

ケイ氏の考えとしては、抽象データ型系列の C++ はオブジェクト指向としては認識していないようであるが(「”I invented the term object-oriented, and I can tell you that C++ wasn’t what I had in mind”」)、型システムそのものについて否定的に見ているわけではないようである。

I’m not against types, but I don’t know of any type systems that aren’t a complete pain, so I still like dynamic typing. – Dr. Alan Kay on the Meaning of “Object-Oriented Programming”

クリエイティビティ

さて、結果的に抽象データ型が主流になったのあれば、それがいわゆる多くの人が認識するところのオブジェクト指向なのだから、今更「アラン・ケイ氏のオブジェクト指向」についてわざわざ考慮する必要があるのかと考える人もいるかもしれない。しかし、筆者にとってこの二つの立場の違いは、単にプログラミング言語の差異に留まらず、コンピューティングというものに対する立場を二分する大きな断絶を象徴する分岐であるように思えるのである。

抽象データ型というのは、単にプログラミング言語の問題である。あるいは、システム開発者側の問題だと言っても良いかもしれない。例えば、開発側の観点から仕事上のトラブルを少なくするといった、よりプラグマティックな理由から型システムという仕組みを評価できるだろう。職業プログラマの間で型システムが普及した理由にはこのような側面もあったのではないだろうか。

しかし、アラン・ケイ氏による1993年の論文、オブジェクト指向が生まれる過程が仔細に書かれた「The Early History Of Smalltalk」を読むと、オブジェクト指向は単にプログラミングだけの問題ではなく、もっと壮大な理想を追求するために考案されたのだということが分かる。

オブジェクト指向が生まれる直前の60年代という時代は、コンピュータが、予め計画された計算を行うだけというイメージの、一つの部屋を専有する巨大な機械から、もっと人間の力を高める形でインタラクションを提供する新しいメディアへと生まれ変わろうとする、いわゆるパーソナルコンピューティングの黎明期に当たる。そして、そのような運動の中心にあったのが、ARPA(Advanced Research Projects Agency)というアメリカ国防総省管轄の研究機関である。ARPA はインターネットの原型となった ARPANET を開発したことでも知られる。

このパーソナルコンピューティングを模索する流れの中で、アラン・ケイ氏が立ち上げたのが Dynabook 構想であり、この構想には今日では当たり前になっているパーソナルコンピュータやネットワーク、GUI(ウィンドウシステム)など、パーソナルコンピューティングにとって重要なあらゆる要素が詰め込まれていた。

パーソナルコンピューティングの主題は、コンピュータを使っていかに人間の能力を高めるか(「Amplify human reach」)ということであったが、これは言い換えれば「クリエイティビティ(創造性)」の追求である。ケイ氏が最も熱心に取り組んでいたのはコンピュータを使った子供の教育であった。オブジェクト指向プログラミングを子供に教えることによって、ソフトウェアを自身の問題に合わせて自在に変更出来るようにし、より高い問題解決能力を獲得させることを目指した。

この教育プロジェクトの試行錯誤によって得られた知見には大変興味深いものが多い。例えば、Smalltalk がクリエイティビティに寄与する一つの根拠として、より少ない原則で多くを表現できるというものがある。有名な「Everything is an object」というやつである。後のケイ氏であれば「Everything is a message」と言ったかもしれない(そのようなプログラミング言語が実際に存在する)。この全てがオブジェクトであるという原則と、大きなシステム(オブジェクト)は小さなシステム(オブジェクト)の組み合わせで作られるという「再帰的デザイン」によって、どんな複雑なシステムをデザインするときでも、覚えなくてはならない原則は少なくて済むようになる。さらに Smalltalk において重要なのは、システムを使うという行為と作るという行為が全く同じになるということである。システムを使うときと全く同じ操作で、その延長線上でシステム自体を変更することができる。このように、導入において覚えることが少なくて済む、そしてその少ない道具立ててであらゆることが表現できるという枠組みが、子供の教育にとって重要であることは想像に難くない。しかし、実際に試してみて分かったことは、あるオブジェクトにメッセージを送るという1ステップの変更については小さな子供でも理解することが出来るが、複数ステップの変更を組み合わせないと解決できないような問題になると、それがほんの2、3ステップだったとしても極めて難しくなってしまうということだった。これは子供だけでなく、プログラミング経験のない大人でも似たような現象が見られたようである。初歩の問題は容易にクリアできるが、問題が複雑化すると、それがプログラマから見たら些細だと思われる問題でも全く歯が立たなくなる。これは今で言うプログラムデザインの問題である。このデザインの教育に対応するためにケイ氏の同僚である Adele Goldberg 氏が「design templates」という仕組みを考案した。これはぼんやりとしたデザインのアイデアとプログラムによる実例の中間に位置する道具立ててで、今で言うところのデザインパターンやフレームワークに相当するものである。

ケイ氏は、このような大きなビジョンを掲げることの重要性について、そして出来るだけ多くを「work in progress」にすることの重要性について繰り返し語っているが、それはいかに多くの技術者や専門家が手段に埋没し、そして宗教論争に明け暮れているかということに対しての警鐘にもなっている。

スティーブ・ジョブズ

「The Early History Of Smalltalk」の一つのハイライトは、1979年、ケイ氏の勤めるパロアルト研究所(PARC)に、あのスティーブ・ジョブズ氏が訪れる場面である。当時のジョブズ氏は77年にアップルコンピュータを設立したばかりで、次世代のパーソナルコンピュータを生み出そうと Lisa プロジェクトを立ち上げていたが、決定的なアイデアがなく模索中の段階であった。ケイ氏は、PARCに訪れたアップルの面々に対して、ALTOというパーソナルコンピュータ試作機のデモを行う。ALTO には Smalltalk による OS が搭載されており、その上ではウィンドウベースのGUIが動いていた。

Xerox Alto Computer
Xerox Alto Computer

そのデモの最中、ジョブズ氏は、試作機で動いていたウィンドウシステムのスクロールをもっと滑らかな方式に変更出来ないかと指摘し、開発者の一人である Dan Ingalls 氏がその場で修正して訪問者を驚かせたという場面が紹介されている。Smalltalk 環境の強力さが垣間見えるエピソードである。

この邂逅の後の歴史は多くの人の知るところなのでここでは深追いしないが、1995年に行われたインタビューにおいて、ジョブズ氏がパロアルト研究所で見たデモについて、そしてその当時は理解出来なかったというオブジェクト指向の重要性について語っているので、その動画を紹介しておきたい。

この映像は放送後に一度紛失したと思われたマスターテープがジョブズ氏の死後に偶然見つかったということで、95年の放送時はカットされた部分も含めて全編放送されたものであるが、その内容にはただひたすら圧倒されるのみである。

このインタビューの中で、1995年当時、今後10年間で最も重要だと思われるテクノロジーとして、ジョブズ氏はオブジェクト(指向)と Web の二つを挙げている。

そして現在

オブジェクト指向についてその起源から検討してみたが、この歴史を前提に改めて考えてみると、昨今言われる「オブジェクト指向から関数型へ」という話が大分狭い領域の話であることに気づかされる。モジュールシステムの進化という観点から言えば、関数型プログラミングが有効である場面が増えているのは疑いようがない。しかし、より大きなスコープで考えるとオブジェクト指向の考え方は依然として重要であり、将来的には、以前紹介した「Functional in the small, OO in the large」という形で適材適所に住み分けることになるのではないだろうか。

そして、アラン・ケイ氏のオブジェクト指向という観点からより視野を広げて考えると、その枠組みにおいてオブジェクトはプログラミング言語とは切り離されたメタファーあるいはコンセプトに過ぎず、メッセージや遅延束縛(late binding)と言ったコンセプトは形を変えてあらゆる場面で見かけるようになった。その代表的なものの一つが、アジャイルという考え方である。オブジェクト指向はその出自から考えても分かるように、より上流の考えを重視する目的指向のパラダイムである。アジャイルはその後、経営やデザインなど、ソフトウェア開発の枠組みを超えて、クリエイション一般に適用出来るような普遍的な考え方として普及しつつある。

プロダクト開発とアイデア信仰 | ゆびてく

Elixir試飲録 (4) – オブジェクト指向と関数型の違い

古いブックマークを漁っていたら、オブジェクト指向と関数型の違いに関する面白い記事を見つけた。

記事は Michael Feathers 氏の興味深いツイートから始まる。

  • 試訳
    • オブジェクト指向は、可変部分をカプセル化(隠蔽)することによって、コードを分かりやすくする
    • 関数型は、可変部分を最小化することによって、コードを分かりやすくする

筆者なりに解釈すれば、オブジェクト指向はインターフェースにフォーカスし、可変部分を捨象することによって複雑なシステムを単純化し、関数型は副作用のない関数を組み合わせることによってシステムの可変部分を最小限に保ち、システムの動的な性質を掴みやすくしようとする。

オブジェクト指向の「可変部分を捨象する」という考え方がシステムを単純化する一方、インターフェースの裏側で起こることは予測し辛くなる。インターフェースの「契約」に現れない状態変化、極端な例を挙げれば「重要なデータを削除しないこと」など、それら全てについて自動テストを用意するのは現実的ではないため、オブジェクト指向のシステムが提供出来る信頼性には自ずと限界があることになる。

そして興味深いのは、オブジェクト指向の上に関数型の仕組みを乗っけるのは止めた方が良いという主張だ。この記事を書いた John D. Cook 氏は、関数型のメリットを享受したいのなら、まずは関数型だけに集中し、プログラムの大きなパーツ(モジュールなど)を管理する時にオブジェクト指向的な枠組みを利用すれば良いのではないかと書く(「functional in the small, OO in the large」)。

コードの中に、関数型の部分とそうでない部分が混在してしまうと、全体としては関数型の恩恵を受け辛くなるのだろう。この話題はこれからもちょくちょく出てくると思うが、Scala と Elixir の比較という観点からも興味深い指摘である。Scalaは基本的にオブジェクトから出発して関数型の流儀を徐々に入れて行くの対し、Elixirはまず関数型から出発する。そして、そこから関数をグルーピングするための Module や、Moduleにインターフェースの仕組みを導入するための Behaviour、そしてポリモルフィズム(多態)のような仕組みを実現する Protocol などが出てくる。

James Hague 氏は、100%純粋な関数型を実現するのは非現実的であり、大体85%ぐらいを目指せばいいのではないかと主張する。そして、残りの15%は注意深く分離してコードの中に分散しないようにすべきであると。

副作用がある部分を切り離すデザインは、Phoenix Frameworkで採用されているデータアクセスのフレームワーク、Ectoの中にも見られた。Ectoでは副作用のない Model と、副作用を担当する Repository を分けるという、いわゆる Repositoryパターンが採用されている。Railsの Active Record が Model 自身に副作用を内包するのとは対照的である。

Elixir試飲録 (5) – Elixir/Phoenixのホットデプロイ完全自動化(2016年1月版)