関数型プログラミング言語でオブジェクト指向プログラミングは行えるか?
Railsでたらたらと開発をするのも疲れてきたので、現実逃避をしてみる。
関数型プログラミング言語でオブジェクト指向プログラミングを行えるかどうか、という問題を考えると、なんとなく、できなさそうな感じがする。この「できなさそうな感じ」はどこから来るのか、考えてみた。
オブジェクト指向プログラミングというと、あなたはどんなものを思い浮かべるだろうか?例えば、オブジェクト指向なスタイルでテキストエディタを作ることを考えてみよう。
簡単なエディタであれば、オブジェクト指向で設計するなら、ウィンドウオブジェクトがあって、その中にはTextViewオブジェクトがあって、テキストの内容自体はTextBufferオブジェクトで管理されていて、メインループがあって、キー入力があるとコールバック関数が呼ばれ、その中でTextBufferオブジェクトの中身を適切に書き換え、それに従ってTextViewオブジェクトの表示を更新して…、といった感じだろうか。副作用バリバリだ。
一方、関数型言語と聞くと、「参照透明」「副作用禁止」といったキーワードが思い付く。けど、オブジェクト指向じゃTextBufferとかばりばりに副作用使いまくってるヨ!ダメじゃん、こりゃ関数型言語じゃオブジェクト指向は無理だ!という話になる、のではないだろうか。ここまでが、理由の考察。ここから先は、それが本当に正しい論理なのかな、という話。
なるほど、さっきのテキストエディタのようなプログラムを関数型言語で実現するのは難しいように思える。しかし、ここでもう少し考えてみる。そもそも、オブジェクト指向ってなんだろうか?
クラスを使って複数のデータをひとつにまとめる、という機能を一番に思い付くけれど、これはオブジェクト指向の本質ではない。だって、データをまとめるだけなら、C言語の構造体で十分だから。(そもそもクラスという概念がないオブジェクト指向言語もある。)
一般に、オブジェクト指向プログラミングに使われる機能として「カプセル化」「継承」「多態」が挙げられるらしい。(ある学派では、とつけておいて方が良いのかもしれない。メッセージングがどうのとかそういう話がまったく出てきてないし…)この全部の機能を実現しなければならない、というわけではないようだ。この中で重要なのは多態、つまり違う種類のオブジェクトに対して適切な処理を呼び出す機能であるように思われる。カプセル化はプログラミングスタイルの問題だし、継承はまぁなんというか、多態が適切に使えるなら、あってもなくても良いような気がするので。
ともかく、オブジェクト指向プログラミングには多態が一番重要であると仮定する。さて、ここで純粋関数型言語と形容されるHaskellの事を考えてみる。Haskellには多態があるか?ある。例えば、+という演算子は多態的だ。1 + 1という計算(整数の足し算)にも使えるし、1.0 + 1.0(実数の足し算)にも使える。演算子だけだと説得力に欠けるので関数も多態だと言う事をちゃんと示したかったが、多態的な関数はreturnとか>>=ぐらいしか思い付かなかった。めんどくさいから探さないけど、探せば他にも多態的な関数はあるだろう。つまり、Haskellは多態的である。
さて、話をオブジェクト指向に戻す。「多態があればオブジェクト指向と言ってよい、とする」「Haskell(純粋関数型言語)には多態がある」から「Haskellはオブジェクト指向と言ってよい」が導かれる。おお万歳、Haskellはオブジェクト指向言語だ!
しかし、なんだかだまされているような気もする。せっかくなので、どこで話が劇的に転回しているかを考えてみよう。「オブジェクト指向は副作用だらけだから関数型言語では無理」から「オブジェクト指向の本質は多態」のところに、さりげなく大きなギャップが潜んでいるようだ。「オブジェクト指向の本質は多態」とする影には「オブジェクト指向は副作用がどうのという話には関係ない」という消極的な主張が潜んでいる。つまり、多態さえ実現できるなら、副作用バリバリのオブジェクト指向があっても良いし、逆に参照透明なオブジェクト指向があっても良い。
「オブジェクト指向の本質は多態!」「オブジェクト指向と参照透明は直交する概念!」この2つを忘れなければ、あなたもきっと関数型言語で参照透明なオブジェクト指向プログラミングが出来ます、たぶん。