動的発展

檜山正幸 (HIYAMA Masayuki)
Fri Jan 28 2005:start
Sat Jan 29 2005:draft

実行時にシステムの能力や構造が変容するという現象について考えてみる。 が、結論は出ない。

目次

1. はじめに

こういう話題もどうかな?と思うのだが、いつも僕が気にしていることだか ら書いてしまおう。「どうかな?」と思う理由は、事情がハッキリとしてなく て結論も出ないような話はどうかな、ということだが、まー、いいや。さて、 その「気にしていること」とは、「システムにどうやって動的/進化的特性を 持たせるのか?」ということだ。この問題意識は極めて一般的な文脈で語られ るべきものだが、例をJanusの言葉使いで提示する都合上、Janusコンポネント のプロファイルについては知っていると仮定する。(Janusコンポネントとそ のプロファイルについては、 「Janus(ヤヌス)の紹介 -- stdin/stdoutからの入門」 」、 「Janus(ヤヌス)の紹介 2 -- フィルターからコンポ ネントへ」を参照。)

2. コンポネントの仕様が動的に変わる例

コンポネントAは次のようなプロファイルHappeningSensorを持つとする。 (ListenerじゃなくてWatcherとしたのは、僕がヘソ曲がりだからだ。)

interface HappeningWatcher {
 void somethingHappend(Happening happ);
}

profile HappeningSensor {
 // inputはなし
 output:
  use HappeningWatcher;
}

図で描けば、次のようになる。

FIG: Aは外部事象を検知して伝達する

/* happening-sensor.gif */

このHappeningSensorコンポネントAは、外部事象を検知して、ソフトウェア 的なイベントに変換して伝える役割を持つ。他のコンポネントは、コンポネン トAの出力ポートに接続して、外部事象を監視し、対応する処理を行う ことができる。もちろん、外部事象監視プログラムは、入力側に、provide HappeningWatcher; (-HappeningWatcher)というポートを持たなくてはならない。

以上に述べた設定では、イベントのユニキャストしかサポートしない。これ では不便だ。マルチキャストをやりたいなら、コンポネントAは、可能性と して、あるいは潜在能力として複数のプロファイルを持たなくてはならない。 たとえば、3つの監視プログラムが接続する状況では、次のプロファイルが要 求される。

profile HappeningSensor {
 output:
  use HappeningWatcher; // ポート1
  use HappeningWatcher; // ポート2
  use HappeningWatcher; // ポート3
}

つまり、実行時にプロファイルが変わっていくことも考えなくてはならない。 プロファイル宣言は、単に静的に固定した記述ではダメで、コンポネントの可 能性、あるいは潜在能力としての多数の(ときに無限の)プロファイルを記述 する必要がある。これは、(できなくはないが)けっこうめんどうだ。

もし動的プロファイルの記述ができたとしても、コンポネントの直列結合、 並列結合のような基本演算が極端にややこしくなってしまう。まず、 結合可能性(composability)の判定が難しい。結合可能だとわかっても、結 合が一意的に決まるわけではない。どの結合形態を選ぶか(どことどこをくっ つけるか)に何らかの原則/アルゴリズムが必要になる。

いま例に出したマルチキャスト・イベント通信に関して言えば、実装上では 簡単な解決策がある(みんな、やっていることだ)。が、この例が含意してい る課題はそれほど単純ではない。プロファイルが実行時に変わるということは、 それまで持ってなかった能力やそれまで知らなかった要求が実行時/非同期に アタッチ/デタッチされることがある、ということだ。普通は、コンポネント の能力やコンポネントへの要求はコンパイル時に決定されている。インターフェー スもプロファイルも実行時に変容するかもしれない、となると、これは随分と やっかいな話だ。

3. 個体から集合体へ

前節の話の流れでは、「1個のコンポネントの仕様が、実行時に変動する」 かのごとくに語っていた。だが、必ずしもそう考える必要はない。つまり、個々 のコンポネントは静的に固定した仕様を持つ、という考えを維持することもで きる。

「コンポネント仕様は不変」が大前提のとき、実行時の変容は、どこで実現 されるのだろうか? それは、コンポネント複合体(composition, composite) である。例えて言えば、個人の能力は変化しないが、個人が集まった組織体の 能力が変わっていくと考えることになる。

この発想に沿って、再びイベントのマルチキャストを考えてみよう。が、そ のためには、記事「ETBダイアグラム」に出てきた重 複器(duplicator)と、重複器を多段に重ねた「n倍のコピー」の知識がいる (必要なら復習してね)。記事「ETBダイアグラム」とは記号を変えて、重複 器をΔで表そう。Δのプロファイルは [-HappeningWatcher → +HappeningWatcher, +HappeningWatcher]となる。よって、例えばA;Δのプロ ファイルは[→ +HappeningWatcher, +HappeningWatcher]である。

HappeningSensorコンポネントAに接続するコンポネントをB1, B2, B3として、 次のようなコンポネント複合体の列を考えよう。記号「+」は並列結合である。 (極性の符号にも「+」を使っているので、混乱しないように注意。)B1, B2, B3の入力側は-HappeningWatcherとなっているとする。また、I, J1, J2は適当 なストレート・ジャンクションである。

  1. A
  2. A;Δ;(B1+I)
  3. A;Δ;(B1+I);(J1+Δ);(J1+B2+I)
  4. A;Δ;(B1+I);(J1+Δ);(J1+B2+I);(J1+J2+Δ);(J1+J2+B3+I)
FIG: Aに、B1, B2, B3を接続

/* multicast */

これらの複合体のプロファイルは、[→..., +HappeningWatcher]という形を している。「...」の部分は、B1, B2, B3のプロファイルの影響を受けるが、 最後のポートは常に+HappeningWatcherであり、いつでも最後のポートが接続 可能な状態で空いている。

つまり、こうなる。「XにBを接続する」という行為を、「XとBから、X;Bを作 る」と考えるのではなくて、「XとBから、X;(J+Δ);(J+B+I)を作る」と考える のだ。ここで、IとJは計算の整合性のために導入されたストレート・ジャンク ションだが、実体は長く伸ばしたワイヤーに過ぎない(別に長く伸ばす必要さ えないのだが)。

X, B → X;(J+Δ);(J+B+I) という定型操作は、ある種のワイヤリング手順で ある。個々のコンポネントを変更しているわけではなくて、そのつながり具合 を操作していることになる。

以上に説明した考え方をまとめれば次のようになる。

  1. 最小の構成素(原子に相当)は不変な存在である。
  2. 変容するのは複合体である。
  3. 複合体の変容とは、複合体への構成素の追加/削除、構成素のつながり具 合を変えることである。

4. 動的発展のメカニズムは?

2番目の考え方では、焦点を個体から「個体集合と集合内の個体の関係性」の ほうに移している。このアプローチは、伝統的な(悪しき?)還元論に根ざし ている。つまり、総体としての変容を、構成素と複合体の2階層に分けて、 「不変な構成素+変化する編成構造」へと還元している。この還元で問題がほんと に簡単になったかというと、それは疑問だ。「追加/削除」とか「つながり具 合の変更」という操作を解析することが、相変わらず困難だからだ。 そもそも、「個人 vs 組織」、「基本コンポネント vs 複合コンポネント」 といった階層的対立図式がほんとに成立するのだろうか? そんな単純な階層 性で済むとは、僕にはとうてい思えない。

しかしいずれにしても、「個体の変容」、または「集団の変容」、ひょっと すると「個体と集団が共に変容」という現象を定式化して解析しなくてはなら ない。使える道具はそれほど多くはないが、対話的システムなどを考えれば、 動的発展の構造を明らかにすることは必須のことだと思える。