Mops言語のオブジェクトシステムにはどんな効果があるのか


Forthのオブジェクト指向拡張は数多くある。実は、forthで言語の仕様を変更するのは容易なのである。初心者から一歩踏み出したあたりで、言語実装がforthプログラミングの課題となってくる、とさえいえるほどである。結局は、それでforthにおけるどのような問題が解かれるのかにかかってくる。

Mops言語のオブジェクト指向はNEONモデルといわれる構文を基としている。これは、forthのオブジェクト指向拡張としては最も早期のもののひとつである。実際には現時点でのMopsのオブジェクト指向特性は起源からもうかなり離れてしまっている。とはいえ極めて古い時代の設計が基礎となっていることは否定できない。ところが、偶然ではあろうが、驚くべきことに、比較的近年になって初めて注目された新しい着眼点とみなされている事柄についても、既に簡明な解答が与えられているように見える場合がある。(もっとも、forth系では、このような時間軸のねじれはよくあることだが。)

というわけで、ある程度の期間Mopsのオブジェクトシステムの中でコードを書き、さらに、それを実装してみた経験から、このシステムが見せるいくつかの側面について、実装方法も含めて説明し、解明を試みることにしたい。Mopsのシステムはそれほど錯雑したものではないが、その効果は簡単には説明できないほど奥深いのである。これは、分析的個別問題解決指向が強烈なforthがその背景にあることも関連していると思われるが、その話はここでは触れない。

形式論理位階によるオブジェクト指向分類の試み


Mopsのオブジェクト指向は、動的プログラミングを求めるということを主としていないように思われる。つまり、コンパイル時に行うことを実行時まで延期するという手段によって、メッセージ伝送という局所的な部分で"柔軟性"を得ることには、可能ではあるが、あまり腐心していない。というのも、それだけなら素のforthで十分に事足りるからである。つまり、xtを保存して入れ替えながら実行すれば良いのである。実際、xtをvTableに整理して特定のデータ構造と結びつけるだけで、"動的な"オブジェクト指向機能を導入できるのである。その程度ならば、アルゴリズムとデータ構造に関する入門レベルで簡単に実現できる。Forthの強みである。生のままのForthでできることを主たる利点としてオブジェクト指向システムを導入しようとするのは、無駄であるかナンセンスである。

クラスとそのメソッドを定義するというところまでは、MopsのOOシステムも、標準的にみられる機能をでていない。「できること」を表にして比べると、どうということはない印象になってしまう。Mops OOシステムの驚くべき特性は、そこからうかがい知ることはできない。その特性というのは、実はインスタンス変数と呼ばれる対象の仕様の差異から生じてくるのである。

First Order OO System

実用を考慮したオブジェクト指向システムの議論を見ると、インスタンスとして観念されるのは、属性データの塊という趣が強い。インスタンス変数をフィールドと呼んだりスロット — 全てがリストであるとする見方に相応しい — と呼んだりすることに、その思考前提が垣間見られる。そういった属性データの塊が、その上に稼働するメソッドと組になってクラスインスタンスを構成し、上位クラスに特有のデータ塊は、下位クラスの属性データの部分集合を成すことで引き継がれる。上位クラスのメソッドは、データ塊の部分集合に動作するものとして引き継がれ、下位クラスは追加的な属性データの上にもメソッドを持つことによって、上位クラスを拡張、変形するのである。

このようなオブジェクト指向システムは、具体的インスタンスの属性データを第0オーダー、その類型を定めるクラスとデータを操作する関数としてのメソッドを第1オーダーとみれば、形式論理でいうファーストオーダーシステムを形成していると見ることができる。変数と関数からなる特にオブジェクト指向システムを持たない手続的プログラムと、大差はない。データ指向であること、つまり、属性は違うが同類のデータを持つインスタンスを求心点として、その上のプロセスが集められ、編成されているというところが、オブジェクト指向の処理プロセス指向とは異なる点、ということになるだろう。
構成素(Objects) 位階(Logical Order)
データワード(Variable, Value, Array etc.) 0階(the 0th order)
実行ワード(Executable words (colon-definition etc.) ) 1階(the first order)
クラスインスタンス(Class instances) 0階(the 0th order)
メソッド(methods) 1階(the first order)
クラス(Class (prototype)) 2(1.5)階(the second or 1.5th order)
継承(inheritance) 3 (2.5)階 (the third order or 2.5th order)
この数え方で、クラスはセカンドオーダーになる。クラスというオブジェクトが眼に見える形で導入されなくても、インスタンスを中心にメソッドが編成される形が、クローンなどで複製できれば十分である。クラスは数値を返さないので関数ではない。しかし、関数を対象として編成する。見方によっては、インスタンスの求めに応じて関数を返すメタ関数とみることもできる。インスタンスと関数を結びつけるのである。その意味で、クラスの論理階数は関数の1つ上になる。とはいえ、一方の端であるインスタンスがデータの塊として0次オーダーで考えられているのが、ややバランスが悪い。この点からみれば、ここではクラスはむしろ1.5階程度に止まっているというべきかもしれない。
Second Orderは「2次」と訳す人もあるようだが、数学ではかなり昔から「階」と訳していたと思う。まあ、分かればどっちでも良いが。
俗な言い方では、階が上がる毎に、いわゆる「メタ階層」に移行するという話である。
このような形のセカンドオーダーオブジェクトが導入されることが、オブジェクト指向によるプログラミングの革新の出発点であろう。
したがって、このレベルでもオブジェクト指向のプログラミングは可能であるが、そこで実際に新しくできるようになることは、あまり多くない。中心的なことは、関数がインスタンスをコンテキストとして持つことができるようになること、であろう。つまり、ひとつの名前で特定される関数が、それに付け加えられるインスタンスのコンテキストに応じて、様々な動作をするということである。ドグマ的に発想すれば大したことのようにも思われるが、実用上は、別のクラスが同じ名前のメソッドを持ちうるというだけの話で、画期的に有用かというと、それほどでもない。

そこで、このオーダーからはみ出さない頭で考えて、なお有用性を強調しようとすると、処理手順を「動的に」組み合わせ変更できるという方向がひとつある。その方向に行くと「Late Bindはオブジェクト指向を便利なものにするには必須」とかいいだすのである。
それどころか「動的」なものはオブジェクト指向的とか言い出すに至っては、知能崩壊としかいいようがない。そこまでいうことはないか。
実際、動的束縛が便利な局面というのはある。けれども、forthで考えると「屋上屋を架す」の類にしか感じられないのである。特別に静的にガッチリと固めなければならない言語であればメリットも感じられるのだろうが。

この方向をもっと伸ばそうとすると、インスタンスの個別的特性に応じて、前処理や後処理を追加したり、処理の順番を変えたりできるような手段があればいい、ということになる。MOPの議論はこの方向でオプションを追加しようという話を含んでいる。ここまでくると、確かに、かなり「動的」という感じは出てくる。逆に言えば、これくらいできないと、例えばLISPなどでは、オブジェクト指向の機構を利用する意味が感じられなかったのではないかと想像される。

抽象的論理平面で考えると、そのような個々のメソッドの束ね方の操作可能性は、1階要素同士の関係を操作しようとするものである。この点で、ここに至って初めて額面通りのセカンドオーダー領域が拓かれたともいえる。つまり、インスタンスは0階という認識のままである「アカデミックな」オブジェクト指向理論では、クラスは中途半端な第2階に止まってしまうので、MOP云々で初めてキッチリ第2階といえるレベルが導入された、ということになるのである。

それでも、ここまでは、ファーストオーダーのオブジェクト指向の発想の中に止まっていてもできる。

クラスないしタイプを経由した、ファーストオーダーのデータとしてのメソッドの結合関係を前提としつつ、その組を対象単位として、組同士を関連づける機構は、セカンドオーダーといってよいと思われる。そのような機構を操作してどうするかを考えるのがセカンドオーダーのオブジェクト指向、というわけである。

言い方が抽象的になってしまったが、セカンドオーダーOOのための機構といえる最も馴染みのあるものは、実は、継承(Inheritance)であることに気づく。継承は、クラスとクラスの関係を操作するものである。その意味で継承はもっとも古典的な第3階要素である。その内容は、内部構造の複製と追加的変形修正である。

Second Order OO System

けれども、継承はもともと静的な秩序を指向する制約的な手段である。その本質上、窮屈なのである。その結果、上に書いたような「オブジェクト=属性データ」思考では、継承はクラスの構成にめり込んでしまい、より高い抽象度の操作性を持つことができないのである。

そこで、もっとカジュアルに使える第3階要素が求められるわけだが、ここに、ちょっと、その歴史的展開に関する話を挿入してみたい。

構造思考脱落の歴史
  • 継承の制約
継承はこれまでにないプログラム操作の手段を与えたものなわけで、初期のオブジェクト指向プログラムの関心が、継承の機能に集中していたように見えるのも、無理もないことなのである。ところが、そこに落とし穴があったことは、多分、今では誰でも知っている話である。その穴を埋めようとした結果として、新機能が続々と出てきて追加されることになったわけである。この過程で、継承、特に多重継承は悪玉に仕立てられてしまった。けれども実際に問題だったのは、継承が発生してきた背景をすっ飛ばして、売りのための「高度なfeature」だけを追求する姿勢だったのだと思う。

  • 継承からの逸脱、そして離脱
継承は階層構造を構成する。それは、静的なデータ構造と序列関係を基礎としている。あるクラスが他のクラスを継承するとすれば、それは核部分に同じ静的構造を持っているからである。そのことがデータ処理の効率をも高める。そのように、継承はその由来から見ても、静的でカッチリと固まった構造を設計する思考を伴うはずのものである。柔軟で動的な処理を支える堅固な骨格のような。

ところが、複数の関数を束ね合わせるための方法が継承ぐらいしかなかったため、「便利な機能を他所から持ってくる」というだけの発想で継承が扱われ始めた。あれもこれもと、便利そうなメソッドを獲得するために多重継承、という話になった。けれども、継承はもともとそんなものではなかった。特に多重継承は構造が複雑になるのだから、予め静的に設計した上で統合するべきものである。だから、あれもこれもというご都合主義発想に向かないのは当然である。(逆にキチンと設計すれば大したことではなく、多重継承の難点などというのは架空の抽象論でしかないのだが、プログラマーは行き当たりばったり人種なのである。そしてformal指向のアカデミズムは「任意に選択した — つまりデタラメの — 場合の問題」を真剣に論ずる。結果、真面目な人ほど「原理的に」騙される。)
もっとも、データ構造に関しても元々動的で緩い種類の言語では、継承も、初めから機能を引っ張ってくるということでしかなかったのだろう。
とはいうものの、異なるタイプ間でメソッドを結合する方法は継承しかなかった。そんなわけで、オブジェクト指向の「新機能」は、当初は「継承を迂回する方法」という体裁で主張されることが多かったのだろう。
delegateでさえ「継承を回避することができる」とかいわれるらしい。継承が負担過剰だったことは明らかである。
継承経由でなく、例えば、友だち同士ならば使えるメソッドをつくるとか、逆に、下位クラスにも使わせないメソッドをつくるとか、あれこれ方法はあった。けれども、決定的だったのは、多分、mix-inであろうと思う。

  • mixin,Traits etc.
mixinは、自分の理解する所によれば、継承関係にないクラスを横から持ってきて、そのクラスのメソッドも混ぜて1つにまとめてしまう方法である。謳い文句は「多重継承なしでも他クラスのメソッドが使える」。実際、そこでの問題は、機能的なまとまりを構成する関数とデータを束ねること、であって、整合的な構造系列の構築などではないのである。だから必要な処理手続を横から借りてきて集めるという方法が、もっとも直接的な問題の解法であるように思われる。

けれども、mixinは横から借りてきたメソッドも、筒抜けに利用できるようにするものである。すると、メソッド名の衝突をどう解決するかという問題が生ずる。同じ名前のメソッドが、上位クラスから継承されたものにも、mixinされた複数のクラスにも、また、当のクラスでオーバーライドされたものとしても存在する、ということがあり得る。そこは、まあ、常識的に優先順位はつくように思うが、メッセージを送るコードを書く側から見ると、ちょっと不安である。デフォルト順位をかいくぐる方法とかも考えると、かなりややこしい。とすれば、mixinしたクラスのメソッドは筒抜けにはせず、なにか特別に符牒を付けてアクセスする方が、多少、文字数は増えても、読み書きの際に何をしているのか分り易く、メンテナンスも容易なのではないか、とも思えてくる。

それはともあれ、必要なメソッドを継承関係の無い脇道から引いてくるというのは、アスペクトがナンタラという考え方に繋がっていると思われる。

Mopsのインスタンス変数特性

上のような展開と対比して初めて気づいたことなのだが、Mopsのインスタンス変数の性格は、プログラミング上に特異な効果をもたらしている。インスタンス変数は、普通は、クラスインスタンスの内部データの1つ1つを指す。Mopsでもこの点に違いはないのだが、そのインスタンス変数は、単なるデータではなくて、他のクラスのインスタンスなのである。呼び名としてはインスタンス変数というものの、NEWされたオブジェクトのレファレンスを格納する変数ではない。名前で特定されるインスタンスそれ自体が埋め込まれているのである。したがって、継承系列外のクラスのインスタンス変数を追加するだけで、その外部クラスのメソッドが当クラスの機能に追加されるのである。インスタンス変数へのメソッド束縛は原則として静的であるから、実行速度のペナルティーもない。したがって、インスタンス変数を宣言することは、クラスの中に他のクラスの機能を埋め込むことであり、クラスインスタンスにとっては、その中に別のクラスのインスタンスの機能を埋め込むことになるのである。

ただし、インスタンス変数のメソッドはmix-inの場合ように筒抜けにはならない。クラスのメソッド定義から、インスタンス変数にメッセージを送ることで、その機能を利用するのである。したがって、定義クラスがインスタンス変数のクラスの特性を持つようになるのではなく、インスタンス変数のクラスの機能が定義クラスのプライベートな内部機能として追加される、ということである。インスタンス変数は、全く独立に動作できるインスタンスではあるが、存在としては当のクラスの内部的機能にはめ込まれている。クラス定義が、次第にハイレベル機能のモジュール作成という特性を強めてくる。これは、アスペクト指向とかいわれているものの基本であるように思われる。この機能の実装は1980年代の初め頃から存在することになるわけだが、あまりそういう観点から見られることはなかったようである。

一般論では、現在でも、クラスのインスタンス変数宣言部分はベタなデータの格納庫づくりとして捉えられ、結果、インスタンスも実態は付属データの塊とされ、関数部分であるメソッドの操作のところにだけ、何かハイレベルな特性を求めようとする発想が、まだ主流ではないかと思われる。いずれにせよ、Mopsのオブジェクト指向の強みは、その発想を超えた所にある。標語的にいえばコンポジションによるモジュラーなコーディングである。インスタンス化は、そのモジュールを現実に生産することである。
これはオブジェクト指向思想の基本であったと思われるが、現実には、そこに到達しているものは以前は乏しかったのかも知れない。
それがいつまでも怪しいOO教説本が尽きない理由かも知れない。商業的宣伝と技術的解明は区別されなければならないはずのものと思うが、
電子技術系での混同傾向は昔から強く、近年は特に甚だしいように思われる。
さて、このような機能を考えると、各構成素の論理的位階が若干変わってくる。
クラスインスタンスは、属性データの塊としての0階要素から、文字通り処理機能を兼ね備えた具体的動作主として、1階要素に持ち上がる。すると、メソッドとインスタンスを関連づけるクラスは、1階要素を対象として操作するという意味で正式に2階要素と見ることができるようになる。

加えて、クラス内で行われるインスタンス変数のコンポジションは、クラス機能同士を束ねるという意味で、第3階要素と見ることができる。
構成素(Objects) 位階(Logical Order)
データワード(Variable, Value, Array etc.) 0階(the 0th order)
実行ワード(Executable words (colon-definition etc.) ) 1階(the first order)
クラスインスタンス(Class instances) 1階(the 1st order)
メソッド(methods) 1階(the first order)
クラス(Class (type)) 2階(the second order)
クラス -- インスタンス変数宣言(Ivars declaration) 3階(the third order)
継承(inheritance) 3階ないし3.5階?(the third or 3.5th order)
このとき、継承は、合成されたインスタンス変数機能を引き継ぎ操作するのであるから、第4階といえないこともないが、構造的設計が必要であることを考えれば、「操作」といえるほどのものでもない。

つまり主張の要点は、Mopsでは、クラス定義がある種のメタクラス定義の特性を持つということである。もっとも、例えば、Rubyなどの新しいオブジェクト指向言語には、多分、同じように解釈できる機構が備わっているであろう。今ではおそらく、特別に先進的な機能ではないだろう。しかし、逆に言えば、MopsのOOシステムの意味が理解される条件が今やっと整ったといえるのではなかろうか。

「背景思考」と実際のコーディング


便利な機能を強調する宣伝思考では、オブジェクトの見方などはどうでもよい話のように思われるだろう。ところが、人様の話を読んだり聞いたりしていると、意外にそうでもないらしいことが分るのである。

継承系列

まずは、実証的な方向から述べてみよう。
Mopsのオブジェクトシステムでコードを書いていると、継承を使う場面とインスタンス変数を使う場面を使い分けることを考えるようになる。インスタンス変数が文字通りインスタンスが持つことになる変数フィールドでしかない言語仕様では、継承とインスタンス変数が機能的に重なるという意識を持つことはないのではあるまいか。
「継承」の原語は「Inheritance」である。系譜学(Genealogy)風の用語であり、「何かが引き継がれる」イメージで理解される傾向にあるように思われる。それで間違いということはないのであるが、クラスを用いて構造的に定義していく仕様では、実はむしろ階層的類別に近いものと考えるべきであろう。クラスの系譜を下ることは、むしろ一般から特殊へと降りてくることであって、親から子へ代々受け継がれる属性というのとは微妙に異なるのである。後者は、むしろ、プロトタイプ-クローン型のオブジェクト指向の発想であろう。もっとも「既存の便利な機能をあれこれ借りてくる」という発想は、どちらとも完全には適合できない。

そのようなわけで、Mopsのオブジェクトシステムでプログラミングする場合、継承は、下位クラスが上位クラスの機能的特殊化であるといえる場合に採用されることになる。機能の豊富化ではないのである。機能の豊富化にはインスタンス変数の追加が利用される。これはいわば異化作用である。
その結果の一つとして、やたらと長い継承系列が無くなる。必要なくなるのである。
小さな変異なら、同じクラス内でも扱える。それはforthの機能を用いて容易にできる。
他のオブジェクト指向言語でも、おそらく、mixin機能がついてきたところで、継承の負担軽減が現実に可能になったのではないかと憶測される。(そして、「継承を使うのはヘタクソ」みたいなスローガンが作られたであろうことは想像に難くない(実際は知らないが)。)

ともかく、上の段に書いたような一般的基準は、コードの構成を考える上でかなり明確な手掛かりになる。
例えば、FILEというようなクラスがあったとして、新しく定義しようとするクラスがファイルを扱う機能を持つとする。そのとき、新しいクラスのインスタンスは、何かファイルといえるようなモノ(対象)であるかどうか、が問題となる。それがある種のファイルであるという場合には、継承すればよい。そうではなくて、別のものだがファイルと密接に関わるものであるというなら、インスタンス変数としてFileを持てばよいのである。
Mopsでは経験からごく当たり前のことである。実際には自分はオブジェクト指向云々を学校とか本とかでキチンと勉強したことなどない。ごく断片的な話と実地でどう考えるかとの組み合わせで思ったことを書いているのだが、お勉強方面でも基本は同じ議論をしているようにも見える。しかも、どうも一般にはかなり応用に入り込んだ話で、理論として勉強して初めてそこに到るものであるらしい。いや、まあ、それは人にもよるのだろうが。それに、自分の見方が全く根本的に間違っているという可能性も捨て切れない。そこは読者諸氏の賢明な判断に委ねざるを得ないわけであるが。

オブジェクトのCompositionという発想

オブジェクト指向の死から

Mopsのプログラミングに慣れると、オブジェクトの合成(Composition)という見方を自然にするようになる。いつも必要になるわけではないが、プログラムでインターフェイスになるような最終生産物的対象は、大抵、そんな感じになる。

ところが、一般に流れている話を見ると違和感を覚えるのは、その合成の発想が、オブジェクトではなく、メソッドのcompositionであるということなのである。つまり関数指向、あるいは、プロシージャー指向である。有意義な作用をするのはいつもプロシージャーであってデータではないからでもあろうし、アカデミクス方面での主流が関数型思考だからでもあろう。しかし、これは、オブジェクト指向の死でもある。

というのは、オブジェクト指向の基礎的な考え方は、次のような表式で表されているはずだからである:
データ構造+アルゴリズム=プログラム(モジュール)=オブジェクト;
プロシジャー指向は、データ構造をすっ飛ばして、処理アルゴリズムだけを考えるコマンド型モデルへの回帰である。モジュラーとか構造型とかいう話の以前の、最古のプログラミング観への退行である。

もっとも、データ構造というのは、コンピューターという機械の機構から出てくる観念である。つまり、計算理論も含めて数学とは無縁のものである。私見としては、そこにソフトウェア科学の工学としての重要な1側面があったと考えるが、数学で数値計算だけを考えると、確かに、データ構造って何?という話になる。邪魔になるだけであるのならば、データ構造の発想など捨ててもかまわない、ということになる。それを捨てても、「手数(てかず)を減らす」という発想は工学的な部分として残るから、学域としての独自性は保つことができる(多分)、と。すると、プログラムは、パブリックとかプライベートとか符牒をつけるだけで区別されたことになるコマンドの集まりを、適宜束ねて扱うものとなる。結局、総体が手続型のモジュール形成、例えば、動的ライブラリの構築とかに還元されてしまう。オブジェクト指向の死である。最も旧式の手続指向が自分たちこそ「最新鋭の」オブジェクト指向だと主張することも可能になっている。プログラミングの歴史は手続指向の圧勝である、ということにでもなるのだろうか。
実際、オブジェクト指向というとき一般に考えられているのはオブジェクトライブラリーの提供である。
そのようなオブジェクト指向は、プログラミングの方法であるというより、プログラミングしないで済ます方法である。生産的手段というより消費材である。
確かに便利だが、そのような便宜提供にはオブジェクトは不要である。マシンのプログラムを学ぶためには、むしろ有害であろう。
機械の仕組みはこの数十年基本的に変わっていない。にもかかわらず、データ構造という概念が捨象できるようになった理由は、おそらく、データの低廉化だろう。それはメモリーが安価になったという量的な意味で低廉化と同時に読み書きの高速化という処理時間的な低廉化が同時進行したことである。そこで起こったのは、データ構造の抽象ではなく捨象である。データの形の流動化、あるいは不定形化である。もちろん、機械に落とす所では不定形なままでは処理できないから、プログラム構成の段階で不定形なデータは、コンパイル時か実行時か、何処かの段階で決まった形を与えないといけない。それがかつては大変な手間であったのが、今では、実行時にそれをしても、体感上は特に問題が無いほどになってしまった。もっとも、本体の処理量が膨大で、ともかく高速で直接的な処理が必要になる場合には、実行時の特定というのは問題にならない。するとコンパイルには膨大な時間がかかることが多いが、それは一回きりなので、よいのである。
このメモリーの低廉化と処理速度向上の話は、プロシージャーの意味での「動的」処理の実用化という話に限定して語られがちであるが、そこで得られた実際の成果というのは、むしろ、データ構造という思考の破砕であると思われる。
形式論理との対応を常に意識していた関数型言語では当初からデータ構造の流動化を指向していたように見えるのは興味深い論点だが、
ここではあまり関係ないのでやめておくことにする。

とはいえ、機械の動作にはデータ構造の思考がなお有効なのである。そして、よく考えれば、人の思考、とくに科学的というか学問的な水準でキッチリと考えようとすると、何かを名指すものの形式構造について意識する必要があることが多いのである(タイプ論はその(やや表面的な)複製である)。素人の物知りと本格的な研究者とを分けているのは大体その点であり、構造無視のゆえに、知識が豊富でもトンデモナイ話に引っ掛かったり、素っ頓狂な話を信じて主張したりすることが多いのである(この危険は、現状では専門家とか学識経験者にまである)。これは機械の構造とは違うものだが、現実に基づくという際のインターフェイス部分の問題として何か象徴的な連関が感じられるのである。計算理論方面では、データは所詮は数値と言うことで均質で不定形なデータを扱うことで済んでいるのではあるが。

ソフトウェアというのが、機械と人とをつないで人が機械を簡潔かつ的確にコントロールできるようにすることを課題としているものだと考えれば、多様な要請に対して機械をキチンと誤りなく制御するためには、データ構造という思考自体は、今も不可欠であると思う。iMopsのドキュメントに、「ExactでRobustな処理のためには構造的思考は今も必要だ」と書いたのは、だいたいそういう趣旨である。だから私見としては、オブジェクト指向、つまり、データ構造とアルゴリズムをきっちりと組み合わせたプログラムをモジュール化したものとしてのオブジェクト、という考え方は、まだ死ぬべきではないと思うのである。...が、...

オブジェクト参照変数との異質性

インスタンス変数は、他クラスのインスタンスでもあり得るべきである。そうすれば、新クラスはメタクラスのように機能できるのである。
(もっとも、基本がforthであることもまた、この作用のために重要な要素であると思われるが。)

ところが、インスタンス変数を属性データの塊として規定してしまう思考からは、その意味が理解できないようである。「インスタンス変数はオブジェクトの参照を格納するポインタ変数であることもできるのだから、そんなことは当然できる」というのである。加えて、そのような参照はランタイムに入れ替えることもできるのだから、より動的であって優れている、と。Forthプログラマーでさえも概してそう思うようである。

「動的(ダイナミック)」云々はナントカの一つ覚え的で、何でも動的ならば良いというものでもなかろうが、それは別としても、確かに、オブジェクト参照をインスタンス変数として用いることで、少なくとも機能的には同じ結果が得られるようにも思われる。
しかし、オブジェクト参照をインスタンス変数にできないオブジェクト指向言語は存在するのだろうか?多分、大抵のものでは可能なのではないかと思う。では、なぜ、mixinのような特殊機能が歓迎されたのだろうか?
かつて、SmalltalkとMopsの双方に詳しい知人に尋ねてみたことがある。すると、インスタンス変数は基本的にオブジェクト自体というよりその参照変数であり、基本データ以外のクラスのインスタンス変数では長いメッセージ系列には実行速度の点でペナルティーがあるなど、あまり便利ではないということであった。ひょっとすると、いちいちインスタンス変数にコンストラクタを呼ばないといけないのかもしれない(良く知らない)。

しかし、思うに、これは、システムの機能というかfacultyやfeatureの問題ではないのである。プログラム構成の基本思想の問題なのである。
オブジェクト参照の変数としてのインスタンス変数という発想からは、クラス定義が諸クラスのバンドルとコンポジションになりうるという考えが、多分、出てこないのである。インスタンスのデータフィールドあるいはスロットには他クラスのオブジェクトの参照も格納できる、というだけでは、まだ何か違った観点に立っているのである。

Mopsにもオブジェクト参照という特別なオブジェクトがある。これは、通常は他所で生成されたインスタンスのベースアドレスを格納する。インスタンス変数にすることもできるが、その場合も、外部からインスタンスを格納するか、特別にNEWしないと、コンテンツが欠けた状態に止まる。この機構は、メッセージを受けるオブジェクトを流動化しつつ無名オブジェクトの束縛を効率化するために事後に導入されたものだが、もし、初めからこれだけがインスタンス変数として許されていたならと想像すれば、確かに、インスタンス変数宣言がコンポジションの一翼を担うという考えは出てこなかったかも知れない、と思う。
すると、ごく部分的なものでしかない他クラスインスタンスの埋め込みというインスタンス変数宣言の仕組みが、プログラムの構成の仕方や言語の総合的セマンティクスを転換する効果を持った、ということかも知れない。
単純に言語の断片的機能や能力の一つとしてみれば、確かに大したことではないのである。
実際にMopsでコードを書いていても、必ずしもコンポジションとしては考えない人もいるようではある。
これはしかし、ある程度の経験というか、その変容を自分の感覚で感じ取る機会がないと、なかなかわからないようなのである。
あるいは、政治的な理由でわからないふりをしているだけかも知れないが。

結語

インスタンスを属性の塊にとどめてメタ方向に延ばしていこうとはせず、プロシジャーないし関数としてのメソッドの動的組み合わせ可能性に集中するオブジェクト観では、インスタンスは、単に、メソッドを選択するコンテキストでしかなくなる。もっといえば、インスタンスは、vtableの切り替えスイッチでしかない。そして、どれだけ多くのタイミングでスイッチ切り替えの機会が与えられているか、ということが誇られるのである。見たまえ、ダイナミックだろ、と。

オブジェクト(と呼べるもの)があること、とか、各要素がオブジェクトであることが重要なのではない。そのような名目には何の価値もない。重要なのは、それがプログラミングにもたらす機能である。機能とは特定局面で使える道具的便宜に限られるものではない。とくにオブジェクト指向システムのような機構にとっては、プログラム構造的なレベルに、その主要な機能があるはずのものである。私見では、クラスシステムであろうがプロトタイプシステムであろうが、コンポジションによるモジュールの逐次的拡大ができないものは、オブジェクト指向システムと呼ぶに値しないし、そのような発想でプログラムを組み立てたことのない人は、オブジェクト指向プログラミングの経験はないのだというべきではないか、とさえ思う。

Mops言語の観点からは、まとまったモジュールの構成と、その構成の意味付けに関する抽象化の能力を高めることが、最重要の関心事である。ダイナミック云々は、モジュールの機能的完結性とコードの再利用可能性などの点から、必要に応じて対応できればそれでよい。実際、可変性や柔軟性の面では、コードをデータとして扱い、適宜実行できるforthの仕組みによって、既に十分に与えられているように思われるからだ。

小さく機能的にまとまったモジュールとしてのワードをキチンと組み上げていく、というのが、Mopsの基底言語であるforthの流儀であるとすれば、MopsのOOシステムは、機能単位としてのモジュールの意味的抽象化能力を一定方向に更に高めているという点で有効なのだ、と考えるのがMopsプログラマーとしての観点である、と思う。
これが、おそらく、他のページでも書いた、Mopsのオブジェクト指向が、forthの発想の自然な(ただしある特定方向へでしかないが)拡張のように感じられる根底にある思想なのだ。

結局、Mopsのオブジェクト指向は、forthが本来持っているモジュラリティーの思想を基に、より複雑な固有データ構造をモジュールに追加して組み合わせることを可能にし、結果として、機能的な意味単位としてのモジュールの抽象度を螺旋状に高めることを可能にしているのである。


最終更新:2013年12月12日 10:05