Smalltalk-72で学ぶOOPの原点:文字列操作…の前にクラス定義について
アラン・ケイの“オブジェクト指向”というアイデアをもとに(非同期処理などいろいろ足りていないながらも──)比較的忠実に実装された1970年代の非常に古いSmalltalk-72で遊んでみるシリーズです(なお最新のSmalltalkについては Pharo などでお楽しみください!)。他の記事はこちらから→Smalltalk-72で学ぶOOPの原点 Advent Calendar 2019 - Qiita
(条件分岐以外の制御構造からの続き)
文字列クラス
今さらですが、新しい言語に挑戦するときに定番の「Hello, World!」がまだでした。
では早速Smalltalk-72で「Hello, World!」を…に対して皆さんが即座に無意識のうちに「文字列を出力するには文字列オブジェクトにどんなメッセージを送れば良いのだろうか…」と考えを巡らしていたなら、それはもう、メッセージングのオブジェクト指向への扉は開かれていると考えてよいのではないでしょうか。もしそうなら素晴らしいことですね!
それはさておき、どんなメッセージを送ればよいかについては例によって定義を読むのが早そうです。まずは文字列リテラルにis?
メッセージを送って、クラス名を尋ねてみましょう。?
の入力は?
そのままではなく~
(チルダ)を使ってください。
これはオーソドックスにstring
であるとのことですので、ALLDEFS(ブートストラップコード)からto string …
という記述を探して見てみましょう。
黄色くマークしたプリミティブ(始原関数)CODE 3
のおおよその内容は、薄い赤でマークしたコメント部分(文字列リテラルを代用)に擬似コードで書かれています。
クラス定義の要素
Smalltalk-72のやたら絵文字やら記号の多い癖のあるコードにもだいぶ目も慣れてきたと思うので、ここでちょっと脱線してクラス定義の式の構成について触れておきましょう。直近で取り上げたif
やfor
アクションの定義と同様に、クラスの定義にはto
アクションにメッセージを送る式で記述します。
実はクラスとアクションは本質的には同じもので、両者の違いは手続きの中でisnew
アクションをコールしているかどうかという点だけです。したがって、クラス定義式の最少構成は(仮にクラス名をfoo
とした場合)次のような式になります。
to foo (isnew)
クラスやアクションを定義する際に()
内に記述された手続き(これぞ当初の「メソッド」)は、クラスやアクションそれ自身や、クラスの場合はそのインスタンスがアクティベート(メッセージのトークンからの実体化)されたときにコールされるようです。
ここまで出てきたif
やfor
などのアクション、vector
やstring
などのクラスの定義がそうなっているように、()
内の処理、つまりメソッドは、メッセージのパターンマッチ式(∢<マッチさせたいトークン>
)に続く条件分岐処理(⇒(非偽時処理>) <偽時処理>
)の偽時処理にまた新たなパターンマッチをネストする構成になっています。つまり、今のSmalltalkを含め他の多くのOOPLがそうであるように、メンバー関数(便宜的に「メソッド」と呼ばれている──)がメッセージに対応させて個別に定義されているわけではない、ということはすでに述べたとおりです。
あいにくisnew
アクションの定義はALLDEFSにもプリミティブCODE 5
としか示されておらず、擬似コードもないのでその処理内容は想像するしかありませんが、おそらく、☞x ← foo.
などとクラスとして(つまりは、インスタンスを生成するアクションとして)コールされた場合はisnew
は新しいインスタンスを返し(したがって、続けて条件分岐処理⇒()
を書くと、多くの場合、非偽時処理として括弧内に記述された初期化の手続きが実行される──)、そうでなければfalse
を返す仕様になっているのだと考えられます。
変数の宣言
to <クラス名>
の後は、メソッド中で使われる一時変数の宣言が続き、必要なら:
(コロン)を挟んでインスタンス変数の宣言、さらに必要ならもう一つ:
を挟んでクラス変数の宣言を挿入することができます。
ここでちょっと注意したいのは、「Open the ST-72 Manual」ボタンでダウンロードして読むことができる「Smalltalk-72 Reference Manual」(1976年刊)には、この一時変数、インスタンス変数、クラス変数の区切りに|
(バーティカルバー、パイプ文字)を使うように記述されていることです。
これは、1974年に使われていたことが冒頭に記されているALLDEEFS(ブートストラップコード)、および、このエミュレーターで動いているメモリダンプベースの実装と、件の1976年刊のリファレンスマニュアルが対象にしているSmalltalk-72処理系実装との間には明らかな齟齬があることを示しています。したがって、マウスのクリックがフックできなかったり、ファイルの入出力に対応していないなどのエミュレーターそれ自体の不備や制限に加え、処理系がマニュアルのそれより古いことによりうまく動かないコードがあったり、動くとしてもある程度の読み換えが必要である可能性を念頭に置かなければならないことを意味します。
なお、クラス変数がクラスおよびそのインスタンス群から値を参照したり代入したりできる共有変数なのは今のSmalltalkやRubyなどと一緒です。この次の世代のSmalltalk-76から、SIMULA 67スタイルのクラス(つまり、メンバー関数の動的コールをメッセージと称する「省コスト化」を伴って──)が採用されて以降は、同時に導入された継承機構との兼ね合いでトラブルメーカーとなるわけですが、これはそのそしりを受ける前の話ということになります。
string
クラスにはsubstr
というクラス変数が定義されていますが、その使い方が継承機構に絡めて興味深いので、これについては追々ご紹介しようと思います。