ここ半年ほど考えていた問題に解決の糸口が見えたのでメモ。主要なプログラミングのスタイルには二通りあります。一つはソースコード式、もう一つはイメージ式です。
ソースコード式は普通にソースコードを書いてから実行する方法です。イメージ式というのは馴染みが薄いですが、プログラムと開発環境が一体になっていて、プログラムを動かしながら動的にプログラムを変更して行きます。作業が一段落すると現在の作業内容をイメージファイルという大きなファイルに書き出して作業を終えます。プログラムの配布にはイメージファイルをそのまま使います。これは Smalltalk で使われる方法で、LISP でも似たような機能があります。
Smalltalk でプログラムを書いた事が無い人は、ずーっとデバッガを実行しっぱなしで、しかもソースコードをいつでも書き換えられる状況を想像すると良いです。この方式の良い所は、プログラムの生きた振る舞いを直接観察しながらコードが書ける事です。
この便利なイメージ式ですが、重大な欠点があります。長く使っていると段々イメージが秘伝のたれ状態になって、何がなんだか分からなくなってくるのです。そのため、Smalltalk では何故か知らないけど何となく動いていて、意味不明だけど壊れるのが怖くて誰も触れないという事がつい起こりがちです。特にソースコード式より問題が起こりやすい原因として、依存関係の複雑さにあるのでは無いかと気がつきました。
ソースコード式のプログラムでは、実行時に書いたプログラムを次のように頭から順に読み込みます。
- プログラム 1
- プログラム 2
- プログラム 3
この読み込む順序はそのままプログラムの依存関係を示しています。つまり、単純に考えるとプログラム 2 はプログラム 1 に依存しているけど、1 は 2 に依存していないと考える事が出来ます。
一方でイメージ式というのはプログラムを実行しながら依存関係にとらわれず次々とプログラムを書き換えます。
- クラス 1、クラス 2、クラス 3 があったとする
- クラス 2 の変更...
- クラス 1 の変更...
- クラス 3 の変更...
するとソースコード式のように自然と出来る依存順序が無く、ついつい絡まったコードが出来やすいのです。なぜならイメージの変更によって生まれる依存関係は、プログラマがプログラムを変更した順序によって生まれるからです。つまり後から書いたコードが先に書いたコードに依存するという関係になり、論理的な設計とは関係なくなります。ライブラリを書く人は不用意な依存が生まれないように注意深く書かないといけません。
この問題を解決するためには開発環境の意味論について考える必要があります。普通プログラム意味論というと出来上がったプログラムの振る舞いを考える事ですが、そうじゃなくてプログラムが変更されて行く過程について考える必要があるのです。
はっきりしているのはここまでで、あとは想像です。
一つのアイデアをあげると、プログラムの依存順序と開発順序という二つの軸を用意します。最初プログラムは依存順序に基づいて読み込まれ、実行されます。
- モジュール 1 読込 -> モジュール 2 読込 -> モジュール 3 読込
次に、このプログラムを動的に変更する手順として二通りの可能性があります。一つは依存軸に沿って変更する方法。
- モジュール 1 読込-> モジュール 2 読込 -> モジュール 3 読込 -> モジュール 1 変更 -> ...
これは現在の Smalltalk の方法で、モジュールの依存関係を変更してしまいます。モジュール 1 がモジュール 3 の影響を完全に受けないようにするには、もう一つは実行しながら新しい依存軸を作って過去にさかのぼってモジュールを修正すれば良いです。
- モジュール 1 読込 -> モジュール 2 読込 -> モジュール 3 読込
- モジュール 1 読込 -> モジュール 1 変更 -> モジュール 2 読込 -> モジュール 3 読込
- ...
つまり、実行順序が二次元ある平行時間モデルと言えます。このような平行時間モデルを使うと、依存関係を壊さないでプログラムをダイナミックに修正出来ます。問題点を挙げるとすれば、少なくとも開発環境の一部は平行時間の外で動作する必要があるのでメタ性が薄まってしまうとか、修正したクラスのインスタンスを全て作り直さないといけないとか、色々あります。
しかし論理的にこのような動作をすれば良いというだけなので、逆時間デバッガを作るような大それた仕事でも無いと思います。今の所、これが私の思いつく唯一イメージ式の生き残る方法です。