Smalltalk-72で学ぶOOPの原点:文字列操作…の前にクラス定義について

アラン・ケイの“オブジェクト指向”というアイデアをもとに(非同期処理などいろいろ足りていないながらも──)比較的忠実に実装された1970年代の非常に古いSmalltalk-72で遊んでみるシリーズです(なお最新のSmalltalkについては Pharo などでお楽しみください!)。他の記事はこちらから→Smalltalk-72で学ぶOOPの原点 Advent Calendar 2019 - Qiita


条件分岐以外の制御構造からの続き)

文字列クラス

今さらですが、新しい言語に挑戦するときに定番の「Hello, World!」がまだでした。

では早速Smalltalk-72で「Hello, World!」を…に対して皆さんが即座に無意識のうちに「文字列を出力するには文字列オブジェクトにどんなメッセージを送れば良いのだろうか…」と考えを巡らしていたなら、それはもう、メッセージングのオブジェクト指向への扉は開かれていると考えてよいのではないでしょうか。もしそうなら素晴らしいことですね!

それはさておき、どんなメッセージを送ればよいかについては例によって定義を読むのが早そうです。まずは文字列リテラルis?メッセージを送って、クラス名を尋ねてみましょう。?の入力は?そのままではなく~チルダ)を使ってください。

f:id:sumim:20191207024638p:plain
文字列リテラルにクラスを尋ねる

これはオーソドックスにstringであるとのことですので、ALLDEFS(ブートストラップコード)からto string …という記述を探して見てみましょう。

f:id:sumim:20191207024936p:plain
「string」クラスの定義

黄色くマークしたプリミティブ(始原関数)CODE 3のおおよその内容は、薄い赤でマークしたコメント部分(文字列リテラルを代用)に擬似コードで書かれています。

クラス定義の要素

Smalltalk-72のやたら絵文字やら記号の多い癖のあるコードにもだいぶ目も慣れてきたと思うので、ここでちょっと脱線してクラス定義の式の構成について触れておきましょう。直近で取り上げたifforアクションの定義と同様に、クラスの定義にはtoアクションにメッセージを送る式で記述します。

実はクラスとアクションは本質的には同じもので、両者の違いは手続きの中でisnewアクションをコールしているかどうかという点だけです。したがって、クラス定義式の最少構成は(仮にクラス名をfooとした場合)次のような式になります。

to foo (isnew)

クラスやアクションを定義する際に()内に記述された手続き(これぞ当初の「メソッド」)は、クラスやアクションそれ自身や、クラスの場合はそのインスタンスがアクティベート(メッセージのトークンからの実体化)されたときにコールされるようです。

ここまで出てきたifforなどのアクション、vectorstringなどのクラスの定義がそうなっているように、()内の処理、つまりメソッドは、メッセージのパターンマッチ式(∢<マッチさせたいトークン>)に続く条件分岐処理(⇒(非偽時処理>) <偽時処理>)の偽時処理にまた新たなパターンマッチをネストする構成になっています。つまり、今のSmalltalkを含め他の多くのOOPLがそうであるように、メンバー関数(便宜的に「メソッド」と呼ばれている──)がメッセージに対応させて個別に定義されているわけではない、ということはすでに述べたとおりです。

あいにくisnewアクションの定義はALLDEFSにもプリミティブCODE 5としか示されておらず、擬似コードもないのでその処理内容は想像するしかありませんが、おそらく、☞x ← foo. などとクラスとして(つまりは、インスタンスを生成するアクションとして)コールされた場合はisnewは新しいインスタンスを返し(したがって、続けて条件分岐処理⇒()を書くと、多くの場合、非偽時処理として括弧内に記述された初期化の手続きが実行される──)、そうでなければfalseを返す仕様になっているのだと考えられます。

変数の宣言

to <クラス名> の後は、メソッド中で使われる一時変数の宣言が続き、必要なら:(コロン)を挟んでインスタンス変数の宣言、さらに必要ならもう一つ:を挟んでクラス変数の宣言を挿入することができます。

ここでちょっと注意したいのは、「Open the ST-72 Manual」ボタンでダウンロードして読むことができる「Smalltalk-72 Reference Manual」(1976年刊)には、この一時変数、インスタンス変数、クラス変数の区切りに|バーティカルバー、パイプ文字)を使うように記述されていることです。

f:id:sumim:20191207215034p:plain
1976年刊のドキュメントには変数宣言の区切りは「:」ではなく「|」が使われている

これは、1974年に使われていたことが冒頭に記されているALLDEEFS(ブートストラップコード)、および、このエミュレーターで動いているメモリダンプベースの実装と、件の1976年刊のリファレンスマニュアルが対象にしているSmalltalk-72処理系実装との間には明らかな齟齬があることを示しています。したがって、マウスのクリックがフックできなかったり、ファイルの入出力に対応していないなどのエミュレーターそれ自体の不備や制限に加え、処理系がマニュアルのそれより古いことによりうまく動かないコードがあったり、動くとしてもある程度の読み換えが必要である可能性を念頭に置かなければならないことを意味します。

なお、クラス変数がクラスおよびそのインスタンス群から値を参照したり代入したりできる共有変数なのは今のSmalltalkRubyなどと一緒です。この次の世代のSmalltalk-76から、SIMULA 67スタイルのクラス(つまり、メンバー関数の動的コールをメッセージと称する「省コスト化」を伴って──)が採用されて以降は、同時に導入された継承機構との兼ね合いでトラブルメーカーとなるわけですが、これはそのそしりを受ける前の話ということになります。

stringクラスにはsubstrというクラス変数が定義されていますが、その使い方が継承機構に絡めて興味深いので、これについては追々ご紹介しようと思います。

あらためて「Hello, World!」と文字列操作に続く)