トップページ (http://www.symmetric.co.jp/hiyama/)にも記しておきましたが、 2004年の末から、「Chimaira.org」 (http://www.chimaira.org/)というサイトを開設しました。このサイト において、「Chimaira(キマイラ)アーキテクチャ」という技術とその周辺に ついていろいろと書いていこうとしています。現時点(2005年2月)でも、既 にある程度の数の記事 が存在します。今後、檜山正幸の技術記事は主にChimaira.org上で発表します。
しかし、このフォローアップサイト・サイトを閉鎖したり、Chimaira.orgに記事を移動したりするこ とはありません。また、このサイトの更新がまったくなくなるわけでもありま せん。もともとこのサイトは、「JavaWorld DAY 2004 セッションJA-2」のフォ ローアップのためのものですから、長く続ける性格のサイトではありませんが、 それにしても、「増補版テキスト」が半分 のところで中断しているのは心残りなのです。「増補版テキスト」をひととお りは完成させないと、このサイトの目的を達したとは言えないでしょう。
それと、もうひとつ気になっていたことは、記事「アクセッ サについて」の最後で、次のように書いてしまった:-)ことです。
アクセッサという概念はとても単純ですが、つきつめて考えると色々と面白 いことがあります。アクセッサ(あるいはミューテータ)の定式化のなかに、 意外にも、観測行為やシステムの因果律の本質がかいまみえるのです。このこ とは、来月にでも :-)
つまり、アクセッサの続きの話も書かないと、どうも収まりがつかないので すね。それで、この記事で、アクセッサについてさらに述べてみたいと思いま す。
僕はJWD2004の講演で、「アメナブル(amenable; 従順な、素直な)」という 概念を提示しました。その定義は、 「増補版テキストの」の第10節にあります。 それを引用すれば:
コンストラクタ、アクセッサ、ミューテータだけで構成されるクラス/オブ ジェクトを‘アメナブル(amenable)’なクラス/オブジェクトと呼ぶ。
ところが、世の中のクラス/オブジェクトはアメナブルとは限りません。む しろ、アメナブルでないものが多いでしょう。ですから、「アメナブルにする なんて無理」という声もあがるでしょう。それに対するひとつの応えは 記事「アクセッサについて」の第2節に述べました。 つまり、アメナブルな基本インターフェースの上で便利メソッドを書くのはど うぞご自由に、ってことです。
では、ホントに「どうぞご自由に」だけでコトが済んでいるのでしょうか。 ここで、アクセッサでもミューテータでもなく、便利メソッドでもなさそうな 例を出してみましょう。先に結末を言ってしまいますが(興ざめかも知れませ んね)、実際は、比較的簡単にアクセッサとミューテータに分解できる例です。
古典的な入力ストリーム(標準入力stdinです)を考えましょう。C言語なら getcという名の関数で入力を行いますが、今風(?)に、getCharというキャ メルケース(*注1)のメソッド名を使いましょう。EOF(end of file)の判定 は、isEndOfFileという名前のメソッドにしておきます(*注2)。インターフェー スで書くなら次のようになりますね。
interface TextInput {
char getChar() throws TextInputException;
boolean isEndOfFile();
}
名前の先頭文字は小文字として、それ以降は単語の最初を大文字にする命名 方式のことです。それをキャメルケースと呼ぶのは、見た感じがコブのあるラ クダのようだから、ということですが、誰が言い出したのでしょうね。
このネーミングにした理由は、Chimaira.orgの 記事「Janus (ヤヌス)の紹介 2」のノート「独り言」を見れば分かるでしょう。実は、 Chimaira.orgでもstdin/stdoutの例を使ったのですが、C言語の名前getc, putcと、それに合わせた名前を使い続けて、ちょっと苦しかったのです。
上のインターフェースに出てくる入力を行うメソッドgetCharは、値と副作用 の両方を持ちますね。入力用の関数/メソッドの多くは、getCharのように 「値+副作用」を持ちます。ハイッ、それは事実です。
が、次のような2つのメソッドを考えれば、アメナブルにできます。
peekCharとshiftは、入力ストリームをスタックのように考えて、「スタック トップを調べること」と「スタックからポップすること」に対応します。先に 出てきたisEndOfFileは、「スタックが空かどうかを調べること」と同じです。 結局、入力ストリームは、スタックと同様な次のインターフェースを持つと考 えられます。
interface TextInput {
/* 基本メソッド */
char peekChar() throws TextInputException;
void shift() throws TextInputException;
boolean isEndOfFile();
/* 便利メソッド */
char getChar() throws TextInputException;
}
気が付きましたか? 便利メソッドgetCharというのは、 記事「アクセッサについて」の第2節で僕が例に出し た“戻り値付きのpop”とまったく同じですね。こうしてアメナブルなレベル まで“落として”みると、「入力ストリームとは、push操作を除いたスタック なんだ」という事実が明白になります。
Unix風のAPIに含まれるtime()という関数は、1970年1月1日0時(UNIX epochと いう)からの秒数を返すものです。Perlにも同じ関数があります。Perlならコ ンパイルも要らないのでちょっと試してみましょう。
perl -e "print time"
今僕が原稿を書いているこのとき、UNIX epochから1108618129秒経過してい るのだそうです。ナルホド。
さて、このUnix風timeと同じ仕様を備えたメソッドtimeだけからなる次のイ ンターフェースを考えてみましょう。
interface TimeFromEpoch {
long time();
}
このメソッドtimeはアクセッサのようにも思えます。が、呼び出すたびに違 う値を返します(*注3)。思い出してください。記事 「アクセッサについて」の第3節から第6節にかけての議論で、アクセッサ は2度続けて呼び出した場合、同じ値を返さないとダメだ、と話しました。
秒単位ですから、短い時間内にたて続けに呼び出せば、同じ値が返りますけ どね。
そうなると、timeはアクセッサとは呼べません。値があるのでミューテータ でもありません。では、隠れたアクセッサとミューテータから組み立てられた 便利メソッドでしょうか? そうも思えませんね。いよいよ、アメナブル概念 が危機をむかえているのでしょうか。
実は、問題設定にインチキが入っています。いま我々は、 「TimeFromEpochをアメナブルにしよう」という問題意識で考えてますが、ア メナブルになるための前提はあまり問題にしてませんね。最初から絶対にアメ ナブルになりようもない状況が存在します。それは、“自律的に発展するシス テム”です。おっと、どうも難しい言葉が出てきてしまったので、節を改めて 説明しましょう。
“自律的に発展するシステム”について説明しましょう。「自律的に」とは、 外部からの刺激や援助を受けずに、それ自身で、という意味ですね。「発展」は "evolution"の訳語ですが、進歩とか進化という言葉から連想されるような意 味は含まず、単に「変化する」という程度のことです。「システム」は広い意 味でとらえてください。「オブジェクト」、「コンポネント」、「プロセス」、 その他なんと呼んでもいいですが、時間に沿って動作するようなモノは「シス テム」に含まれます。以上のことをまとめれば、“自律的に発展するシステム” とは、「外部からの刺激を受けずにそれ自身で変化するモノ」です。
自律的に発展するシステムにおいては、異なる時刻に発行されたメソッド呼 び出しが同じ値を返すことは保証できません。システム内部の発展法則(自律 的な発展を支配している法則)が分からないなら、その振る舞いを予測するこ とは不可能です。もちろん、発展法則がよく分かっているシステムならば、利 用価値があります。TimeFromEpochは、発展法則がよく分かっている“自律的 に発展するシステム”といえるでしょう。
ここで、「なーんだ、TimeFromEpochは自律的に発展するから、アメナブルに するなんて、しょせん無理だったのか」と短絡的に納得しないでくださいね。 まだ続きがあります。TimeFromEpochのようにタチの良い非アメナブル・シス テムは、システム境界を変更すれば、アメナブルになります。また、アメナブ ルになり得るシステムとは、実はタチの良い“自律的に発展するシステム”な のだとも言えるのです。
先走りし過ぎました。もっと具体的に、そして順番に説明しましょう。まず、 TimeFromEpochをカウンタだとみましましょう。そして、メソッドtime()は、 内部カウンタの値を返すアクセッサだとみなします。では、このカウンタをカ ウントアップ(インクリメント)するのは誰でしょうか。もとをただせば、ハー ドウェア的な機構があって、時間を刻む信号が来ているはずですよね。このカ ウントアップする機構のために、メソッドincrementTime()を公開しましょう。 すると、インターフェースは次のようになります。
interface TimeFromEpoch {
long time();
void incrementTime();
}
つまり、TimeFromEpochを使う我々からは、ミューテータincrementTimeが見 えてなかったわけです。通常、incrementTimeを我々は使いませんから、見え ている必要はありません。でも、時刻合わせをするときもあるので、setTime は入っているべきかもしれません。この要求も取り入れると、さらにインター フェースがふくらみます。
interface TimeFromEpoch {
long time();
void incrementTime();
void setTime(long time);
}
上ののインターフェースを次の純粋な(?)カウンタと比較してみてください。
interface Counter {
long getValue();
void increment();
void setValue(long val);
}
どうですか、TimeFromEpochとCounterは同じですよね。この事実をシッカリ と押さえてください。これを踏まえた話を次節以降でします。
ここで、我々がたどってきた道筋をもう一度振り返ってみましょう。まず、 「扱いやすさ」のひとつの基準としてアメナブルという概念を提示しました。 アメナブルを定義するには、アクセッサとミューテータという概念が必要です。 ですが、アクセッサの条件である「副作用がない」というのは、人間どおしの 信頼関係や約束事でしか解釈できないのではないか?という疑いが生まれたの です。この疑いを晴らしたのが、記事「アクセッサについ て」の後半です。
アクセッサセットの定義をもう一度述べておきましょう。{a1(), a2(), ..., aN()}というN個のメソッドのセットがアクセッサセットであるとは、次の条 件を満たすことです。
アクセッサどうしは、互いにどのように入り混じろうと、呼び出し結果の値 に影響を与えることはありません。アクセッサしか持たないオブジェクトなら、 メソッド呼び出しの結果は完全に決定されていて、アクセッサを定数(引数が あるなら純関数)とみなすことができます。
もちろん、アクセッサ呼び出しのあいだにミューテータ呼び出しで割り込ま れると、アクセッサ戻り値は変わるでしょう。ミューテータは内部状態を変え る操作だし、アクセッサは内部状態に応じて値を返しているのですから。
さて、以上の話の流れには、暗黙の、しかし重大な前提が含まれています。 気づきましたか。次のようなことです。
別な言い方をすると、そのシステムはある利用者により占有されており、他 の(未知の)誰かから影響を受けたりはしないのです。よって、その利用者が ミューテータを呼ばない限りは何事も起こらず、内部状態も一定のままです。
アメナブルという概念の背後には、そのシステムを“閉じたシステム”とし て占有利用できる、という前提が含まれていたのです。
閉じてないシステム、つまり開いたシステムとは、特定できない未知の誰か (何か)の影響を受けてしまうシステムです。また、ひとりの利用者が占有し て、完全に支配することが難しいシステムです。そのような開いたシステムに は、アメナブルという概念を適用できません。
さて、今話題にした「開いたシステム」と、先ほど出てきた「自律的に発展 するシステム」との関係はどうなるのでしょうか。未知の誰か/何かの影響を 受けてしまうことと、自分自身で主体的に変化することは、違うことのように 思えます。しかし、よく考えてください。目の前のシステムが、何もしないの に勝手に変動しているとき、その変動の要因が「未知の誰かの影響」か「自発 的/主体的な意図」かを判断するスベはあるでしょうか。残念ながら、判断の 方法はありません。いつでも積極的・快活に行動しているあなたの友人が、実 際はロボットで、宇宙人が操縦しているのかもしれませんよ。
アメナブルは、一見、システム/オブジェクトが持つインターフェース の構文的な特徴のようですが、そうではありません。本質は、そのシステム/ オブジェクトが閉じていて、完全に単一利用者の支配下に置けるということで す。だから「amenable=従順な、素直な」という形容詞を使っているのです。 手下として使うなら、従順/素直な奴のほうがいいでしょう。僕がアメナブル を推奨する理由はここにあります(*注4)。
理論的な根拠としては、 アメナブルなオブジェクトの振る舞いは、一種の様 相論理の推論を使って予測できることが挙げられます。
余談ですが、僕(檜山)の興味は“開いたシステム”にあります。今説明し た事情から、「開いたシステム」とは「自律的発展システム」だと言っても同 じことです。そのようなシステムは、従順でも素直でもありません。扱いにく い、ちょっとイヤな奴です。でも、友人として付き合うなら、歯ごたえがある 奴のほうがいいでしょう。僕が非アメナブルが好きな理由はここにあり ます(*注5)。
非アメナブルなシステムを調べたいなら、アメナブルなシステムに関する徹 底した解析を経験しないと、とても太刀打ちできないでしょう。僕もそのため にトレーニング中です。
ここでまた、時計(TimeFromEpoch)の例に戻りましょう。TimeFromEpochの インターフェースが、話の流れと共にふくらんでいったので、ここで改めて、 それらのインターフェースに番号を付けて区別することにします。以下のとお りです。
interface TimeFromEpoch_0 {
long time();
}
interface TimeFromEpoch_1 {
long time();
void incrementTime();
}
interface TimeFromEpoch_2 {
long time();
void incrementTime();
void setTime(long time);
}
TimeFromEpoch_2は、このなかでは一番“広い”インターフェースになってい ます。ですが、これは広すぎるともいえます。単一の利用者ではなくて、複数 の利用者に向けたインターフェースをゴチャッと寄せ集めている感じです。こ の事情を明らかにするために、次の4つのエージェント(動作主体、それ以外 の妙な意味はないよ:-))を考えます。
時刻発生器は、一定の間隔で時刻カウンタをインクリメントします。つまり、 時刻発生器こそ、時計の本質です。時刻カウンタは、時刻値を整数として格納 してます。そして、時刻利用者はカウンタに問い合わせ(アクセッサ呼び出し)を することで現在時刻を知ります。最後の時刻設定者は、カウンタ値をセットす る者です。
TimeFromEpochは時刻カウンタですが、このカウンタに“触るエージェント”、 もっと正確にいえば、エージェントのロール(役割)が3つあると考えられま す。インターフェースTimeFromEpoch_2は、ロールを無視して、とにかく誰か が使いそうなメソッドは全部いれたものです。インターフェース TimeFromEpoch_0は、時刻利用者用に限定した、狭いインターフェースです。
一般にインターフェースが狭くなれば、それは用途に特化したものとなり安 全性も増すでしょう。その代わり、そのインターフェースを通して見たシステ ムはアメナブルにはなりません。インターフェースが十分に広いと、システム はアメナブルになり、広いインターフェースを全部占有して当該システムを完 全支配することができます。しかし、広いインターフェースはゴッタ煮風の混 乱したものになりかねません。
ここで僕は、インターフェースが「狭い/広い」という言い方をしましたが、 これは、インターフェースが「大きい/小さい」とは異なります。どういうこ とかというと、あるシステムが備えているインターフェース全体の規模を問題 にするとき、僕は「大きい/小さい」と言います。その全インターフェースの うち、特定の目的(あるいは特定の利用者向け)に区切られたサブセットの 相対的な規模に注目するときに、「狭い/広い」と表現してます。システ ムが全体として1000個のメソッドを持つとき(ウゲッ!)、100個のメソッド からなるサブインターフェースは相対的に狭いのです。
インターフェースの相対的な“幅”の問題は、システム境界の切り方の問題 と関連します。が、話が長くなりすぎるので、この話題は割愛しましょう。
アメナブルな(したがって閉じた)システムでは次が成立します。
これは、「外力が働かない限り、質点は静止し続ける」と似てますね。僕は 実際、上の法則を、システムの力学における慣性の法則だとみなしています。 力学では、慣性の法則は運動の法則であると同時に、慣性系という座標系の定 義にもなっています。つまり、慣性の法則が成立しているような座標系が慣性 系です。これは循環論法のようですが、そうではありません。「慣性系が存在 するのだ」という強い主張が含まれます。
ところで、静止というのは特殊な等速直線運動であって、慣性の法則は「質 点は静止し続ける」の代わりに「質点は等速直線運動をし続ける」としてもい いのでした。あるいは、慣性系を取り替えれば、等速直線運動を静止と見なせ るという表現もあります。システムの力学においても、たぶんこのことは成立 するでしょう。つまり、簡単な発展法則に従うシステムは、観測の枠組みを取 り替えることにより、閉じたシステムとみなせるようです。
観測の枠組みを取り替えるとは、システム境界を引き直したり、インターフェー スの形と幅を変えたりすることです。観測の枠組みの取り替えを系統的に行え れば、なんかいいことがありそうです。僕は、システム(コンポネントと言っ ても同じことです)の特徴付けを、計算の対象とすることにより、観測の枠組 みの取り替えを系統的に行えるだろうと予想しています。その予想が正しい かどうかを試してみて、その結果を報告するために、 キマイラ・サイトChimaira.orgを開設した のです(それが目的のすべてではありませんが)。興味を持たれたかたは、ど うぞお越しください。(と、最後は宣伝でした:-))