九月に思うこと

hiyama(檜山正幸)
Tue Sep 28 2004
Wed Sep 29 2004:わずかに修正
Sat Jan 22 2005:わずかに修正

目次

1. そもそも…

今は9月末 -- 6月の「JavaWorld DAY」からは随分日がたってしまいました。 補足的解説も中途半端に放置状態…。で、ここで、補足とは少し違った形で関 連事項をまとめてみようと思います。

そもそも、「JavaWorld DAY」の話題は、私の連載『XMLボキャブラリの理論 と実践』と連動させる意図がありました。連載の主題はXMLなのですが、XMLだ けに限定されないソフトウェア開発の原則のようなことも書きたいと思ってい たのです。そのような(より一般的な)話題のエッセンシャルな部分を 「JavaWorld DAY」で話そうと思っていたのですが、あまりうまくはいきませ んでした。

一方、連載のほうは、XMLの話と一般論が上手にミックスできなくて、これま た、あまりうまくはいきませんでしたね。そういうわけで、「連動」どころか 「共倒れ」だったりして。ハア(ため息)。しかし、私が扱おうとした話題そ のものは、やはり意味/意義があると思っています。ですから、いくつかのト ピックを、繰り返しをいとわず以下に述べましょう。

2. 基本データ型

私は、なにげなく「基本データ型」という言葉を使ったりしますが、これは ちゃんと定義(少なくとも説明)すべきですね。int, boolean, doubleなどのプリミティブ型は当 然に基本データ型と呼んでいいでしょう。オブジェクト型(参照型)でも、 Stringなどは基本データ型と呼びたいところです。

では、基本データ型であるための条件とはなんでしょうか。私は「数学的な 値」として取り扱えるものが基本データ型だと思っています(そう定義したい のです)。まずは、等号(同値性)がきちんと定義されている必要があります。 等号の記号は、関係演算子「==」でも、メソッドequalsでもなんでもいいので すが、次の3条件は必須です(ここでは、等号として「=」を使うことにします)。

  1. xがなんであっても、x = x
  2. x, yがなんであっても、x = y ならば y = x
  3. x, y, zがなんであっても、x = y かつ y = z ならば x = z

数学を勉強した方は、これらの条件を上から順に「反射」「対称」「推移」 の法則と呼ぶことをご存じでしょう(そういう法則名を憶える必要はありませ んが)。

さらに値とオブジェクトを区別する条件は、ミュータビリティ(変更可能性) です。例えば、整数3がしばらく時間がたつと4になるなんてことはありません。 整数3は時間や場所を超越して3であり続けます。一方、整数3を初期値とする 変数の値が4になることはあります。プログラム変数は時間や場所と関連する からです。つまり、(変数ではなく)“値”となる基本データ型はイミュータブル (変更不可能)でなくてはならない、となります。

3. 時間、場所、メモリ管理

intやbooleanの値(変数ではありませんよ)は、時間と場所に無関係だし、 メモリ管理の対象にもなりません。StringはJVM内のメモリ管理の対象になり ますが、プログラマからは時間、場所、メモリなどを意識する必要はありませ ん。インターンだとか「==とequalsの使い分け」とか言い出すと、Stringのメ モリも意識にのぼりますが、それは余分な話です。

基本データ型がイミュータブルであることから、時間に沿っての状態の変化 を考える必要はありません。生成/消滅もそれほど気にする必要はありません。 Stringデータは実際に生成しますが、もともとあった文字列を使うと考えても かまいません。誰かが共有や再利用しても問題ないので、明示的な破壊も必須 ではありません。

基本データ型に関しては、call by valueとcall by refereceの違いも表面に は出ません(*注1)。別な言い方をすれば、コピーしても参照を共有してもセマンティ クスの差はないということです。

注1

Javaの場合、プリミティブ型はcall by valueで引数渡しされます。つまり、 データ自体のコピーがスタックに積まれます。一方、参照型はcall by reference方式です。データが存在するメモリ領域を指すポインタがスタック に積まれます。

オブジェクトが(値ではなく)オブジェクトである理由/事情は、時間、場 所、状態、同一性(アイデンティティ)などが意味を持ち、それを考慮する必 要があることです。時間/場所と無関係で、状態概念がなく、同一性と同値性 の区別が不要なら、それは「値」といっていいでしょう。そして、値の型が基 本データ型です。

4. アクセッサとその値

基本データ型を明確化した理由は、「メソッドの引数や戻り値は基本データ型 にすべき」と主張したいからです。「そんな無茶な」と思われるでしょうが、 これには但し書きがつきます。それは、「引数や戻り値が、基本データ型にま で分解可能なら、それでよい」というものです。

特にアクセッサの値について考えてみます。私の連載で取り上げた簡単なツ リー構造(DOMもどき)を例にします。tがツリー(のルートノードへの参照) だとして、t.children()で子ノード(サブツリー)のリストが得られるとしま す。t.children()は基本データになりませんが、t.chldren().length()(子リ ストの長さ)とかt.children().first().rootName()(最初の子であるサブツ リーのルートノードの名前)とかは基本データです。

よって、children().length()、children().first().rootName() などをアク セッサと考えればいいわけです。つまり、アクセッサの値がオブジェクトのと き、さらにそのオブジェクトのアクセッサを使って、基本データになるまで繰 り返し適用をするのです。

5. プロパティ

普通の用語法とは違うのですが、値が基本データ型であり、引き数がないアク セッサを「プロパティ」と呼ぶことにします。プロパティはアクセッサですか ら、副作用は持てません。値を返すだけです。

どんなアクセッサセットでも、プロパティセットに直せます。例えば、配列 やリストの項目にアクセスするitem(int n)があったとき、item0(), item1(), item2()のような無限個のプロパティに直せます。さきのchildren()は、 children_length()、children_first_rootName()など、たくさんのプロパティ に分解できます。

無限個のプロパティセットなんて扱えないだろうと思うかもしれませんが、 そんなことはありません。「少数の複雑なモノ」と「多数の単純なモノ」のどっ ちが扱いやすいかはケースバイケースです。個数は増えますが(ときに無限個)、 プロパティは非常に単純で扱いやすいのです。

6. 観測量と座標

複雑な物理現象でも、実際に観測できるのは整数値や実数値(たまに複素数 値)です。そういうスカラー値(しかも近似値)の組で現象が記述されるわけ です。観測されたスカラー値の組は、広い意味での「座標」と言えます。「一般 化座標」なんて言葉が実際にあったりするんですね。

さて、個々のプロパティは、オブジェクトの座標成分みたいなものです。プ ロパティセットは“スカラー”の組を定めるのです。すべてのプロパティを寄 せ集めた量が、オブジェクト状態に対する座標となるわけです。わたしは、 「観測量」なんて言葉も使いますが、座標と同じ事です。また、「外在化した 状態」と言ってもいいでしょう。オブジェクトの内部状態は未知(観察不可能) だが、プロパティの値がその代わりになるからです。

7. 観測的に識別不可能

「オブジェクトはブラックボックス」という立場をとると、オブジェクト状 態を調べる手段はプロパティ(分解されたアクセッサ)だけです。すべての プロパティの値が同じなら、その2つのオブジェクトの状態を区別できません。 ただし、実験観測を何度も繰り返すと違いが出るかもしれません。実験観測 (単に実験とも呼ぶ)とは、外部から刺激を与えてその後で観測(プロパティ を調べる)ことです。

実験の記録は次の形になります。

例えば、int peek()が唯一のプロパティで、shift()が唯一のミューテータで ある「int値ストリーム」を考えましょう。次は実験の記録の例です。

この例では、実験の記録は、事実上、整数の列です。

どのような実験観測に対しても同じ結果を返すなら、その2つのオブジェクト は“同じ”とみなすしかありません。

8. オブジェクトの環境と軌跡

オブジェクトには時間や同一性の概念があると述べました。値とオブジェク トの違いは、「幾何学的な点」と「力学的な質点」の違いのようなものです。 座標が(0, 0)である点と、座標が(1, 0.5)である点は異なる点ですが、1つの 質点が(0, 0)から(1, 0.5)に移動しても同じ質点です。幾何学的な点では、座 標が違えば“違う”点ですが、力学的な質点では“同じ”点が位置を変えたと いうことです。

質点では、時間にともなう変化や同一性(異なる位置にあっても同じ)とい う概念が入ってますね。オブジェクトも質点のようなものです。 そして、 オブジェクトにも質点と同様の法則があります。それは「環境(外部)から 何の刺激も受けなければ、オブジェクトの状態は変わらない」というものです。 自律的/自動的に動作するように見えるオブジェクトも、たとえばタイマーか らの刺激をキッカケに動いているでしょう。一切の刺激のない環境(そこでは 実質的には時間も流れない)では、オブジェクトは静止し続けます。

オブジェクトに“運動”をさせたいなら、刺激が必要です。環境から刺激列 を与えると、それに従ってオブジェクトは状態を変更します。これは、観測量 空間(座標空間と言っても同じ)上の軌跡(連続曲線ではなくて、離散的点列)として観測されます。我々の興味は、刺激列 と軌跡の関係です。

「オブジェクトの初期状態(最初の観測量)がコレコレのとき、コレコレの 刺激列を与えると、このような軌跡を描く」と主張できれば、我々は、その法 則に従う一群のオブジェクト(クラス)を把握していることになります。

9. 基本概念:観測、刺激、反応

観測により我々はオブジェクトの観測量=座標を得ます。刺激を与えること によりオブジェクトに何らかの動作をさせます。この動作を(その刺激に対す る)反応(reaction)と呼びましょう。反応は、状態遷移と応答(response) に分けて考えます。

状態遷移は、刺激の前後の観測で判断できます。我々が頼れるのは観測だけな ので、刺激の前後で観測量が変わらなければ、状態遷移はなかったとみなすし かありません。さて、応答とは状態遷移のように観測で知るものではなくて、 明示的かつ積極的な信号です。例外、イベント、メッセージなどが応答にあた ります。

まとめれば、オブジェクトの基本概念は、「観測、刺激、反応(=状態遷移+ 応答)」となります。例えば、JavaBeansを考えてみましょう。JavaBeans流のプ ロパティ・ゲッターはアクセッサですね。JavaBeans流のプロパティ・セッター と(戻り値なしの)メソッドはミューテータです。そして、 例外やJavaBeans流のイベントが応答になります。

10. 現象と推論

物理のような自然科学では、まず現象があります。その現象から法則が抽出 されます。現象の前に法則があるわけではありません。仮に思弁的な方法で法 則を導き出しても、現象と合わなければ却下されますね。

ソフトウェアでは多少事情が異なります。設計とか仕様策定という作業によ り、先に法則を指定できます。その法則が理にかなったものであれば、実現系 (realization)を構成できます。それが仕様に対する実装なわけです。

“正しい実装”が存在すれば、その実装は仕様を満たしていますから、そこ で生起する現象においては、前もって指定した法則が成立しているはずです。 つまり、法則に合わせて現象世界を創り出すことができます。 その現象世界=実装では、法則から導かれる命題もまた成立しているはずです。

これは、ニュートンの法則から天体の運動に関する命題が導かれ、実際の天 体がそのように運動しているのと同じことです。例えば、「リストに項目を addするとlengthが1増える」と「リストの項目をremoveするとlengthが1減る」 という法則から、 「リストに項目をaddして、その後すぐにremoveするとlengthは変わらない」 という命題が導かれます。

このとき、導く過程では推論が使われています。推論や証明は、形式的手法 のダークサイドかもしれません。形式的手法がプログラムの証明と同一視され ることにより、現場からは嫌われて、役立たずとみなされたのだと私は思って ます。ですから、「証明ありき」という紹介や説明は悪い結果をまねく でしょう。

しかしそれにしても、誰でも推論をしているし、推論なしでは日々のプログ ラミング活動、いや日常生活さえ 成立しません。仕様が基本法則であり、実装がその法則に従う 現象世界であるとすれば、現象世界を調べる手段として実験以外に推論も駆使 したいところです。自動証明系を備えた開発ツールとかが登場すればいいので すけどね。それと、教育の問題も大いにありそうです。

11. そして、それから

簡単なデータ構造 - スタック、リスト、キュー、ストリームなどに上に述べ たような考え方を適用するのはそんなに難しいことではないでしょう。実際に やってみると、いろいろなことに気が付きます。そこで得られた問題意識を持 ちながら、複合されたオブジェクトの記述などを試みれば、さらに意義や課題 が見付かると思います。

ツリー構造などは、あまり複雑でない複合オブジェクトなので、適切な素材 だと思います。そういう理由で、私の連載では「Tiny Toy DOM」を取り上げた のですが、解説の完成度はイマイチでした。「Tiny Toy」なんて命名したので、 いかにもオモチャみたいですが、オモチャと実用の差は(多くの)皆さんが想 像するほど離れていません。実用システムだと、クラス/メソッドの数とか、 制約条件の数が増えるので、量的には困難になるのですが、原理的にはさほど 変わらないのです。量的な困難を軽減してくれるツールがあれば、形式的手法 は十分実用になると私は信じています。