Smalltalk-72で学ぶOOPの原点:配列要素の参照と要素の代入の実装

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


配列要素の参照と要素の代入からの続き)

ALLDEFSであらかじめ用意されたクラスの定義を調べる

では配列要素の参照と要素の代入を例に、Smalltalk-72において、(今のSmalltalkを含む)その後のOOPLで一般的になった「メッセージの一部(セレクタ-)と同じ名前のメンバー関数を動的にコール(…することを「メッセージング」と称する)」とは違ったどんなアプローチで実装されているかを見てみましょう。

「ALTO Smalltalk-72」ウインドウの右側に並んでいるボタンの中にある「ALLDEFS」を押すと、Smalltalk-72が起動時に読み込むブートストラップコードを読むことができます。

f:id:sumim:20191204110129p:plain
ALLDEFS ブートストラップコード

低レベルのクラスのコードはプリミティブ(処理系組み込みの始原関数)のコールの実装だけの場合もありますが、その場合でも直後のコメントとして擬似コードが添えられているのでどんな処理をしているのか程度の雰囲気はわかります。

配列のクラスを調べて実装を探す

オブジェクトのクラスは is? というメッセージを送ることで調べることができます。「?」は「?」キーそのままではなく、 KeyboradHelp にあるように「~」(チルダ)をタイプする必要があるので注意してください。do-it (「\」キー。グリフは「!」)で評価すると vector であることがわかります。

f:id:sumim:20191204110619p:plain
配列オブジェクトが属するクラスを調べる

vector クラスは ALLDEFS では「to vector …」で始まる式で定義されています。起動フェーズごとに2回定義されていますが、2回目の定義を見てみましょう。

f:id:sumim:20191204115441p:plain
vectorクラスの定義(薄い赤でコメント部分を示した)

クラス定義式についての細かなことの説明がまだですが、変数の宣言を終えたあと「 (CODE 3 … 」とあるので、まず、プリミティブ CODE 3 を呼ぶ手続きになっていることはだいたいわかるかと思います。この CODE 3 のおおよその内容は他のプリミティブと同様に直後のコメント(「'」(シングルクオート)で括られている部分。実際は文字列リテラル。つまり文字列をコメント代わりにする運用らしい)に擬似コードで示されています(図の赤い部分)。この中の黄色で囲った部分が今知りたい配列要素の参照と要素代入の実装を端的に表した擬似コードです。

f:id:sumim:20191204120016p:plain
配列要素の参照と要素の代入の擬似コード

目玉マーク(∢)と条件分岐(⇒())、そして、評価して取り込み(:)

目が慣れないと何が書いてあるのかさっぱりですが、メッセージとして送られたトークン列を見るパターンマッチ式である「∢<マッチさせるトークン>」と、条件分岐「<偽・非偽値>⇒(<非偽時処理>)<偽時処理>」、メッセージの部分を構成する式を評価して値を取り込む「:<取り込む先の変数>」、そして今のSmalltalkと同様に値を返すリターン(ただしグリフは「↑」や「^」ではなく「⇑」)が分かれば、おおよそこの擬似コードが伝えたいことはくみ取れるかと思います。

つまり、読み下すとこんな感じになります。

  • 送られてきたメッセージの冒頭で [ がマッチしたら…( ∢[⇒(… )
  • 続くメッセージ中の式を評価して結果を一時変数 x に取り込み…( :x. )
  • 括弧を閉じるのに使われるはずのトークン ] を消費( ∢] )。
  • 残りのメッセージ冒頭で←がマッチしたら…( ∢←⇒(… )★
  • 続くメッセージ中の式を評価して結果を一時変数 y に取り込み… ( :y. )
  • y を返す( ⇑y )…のと並行して配列の x 番目の要素を y で置き換える。
  • ★でなければ、配列の x 番目の要素を返す。

[ ] や [ ]← といった名前のメソッドが定義されていてそれを呼ぶ、Smalltalk-76やRubyのアプローチとはずいぶん違った実装になっていることを(まあ、これはあくまで擬似コードなので実際はもう少し複雑なことをやっていますが雰囲気だけでも)感じ取っていただけますでしょうか?

メッセージを受け取ったオブジェクトがそれを読み解いてゆく様子が(独立した関数などでない、文字通りの──)“メソッド”として、BNF法で文法を記述するように、あるいは、Lispのリーダーマクロに近い感覚で記述されていて面白いですね。同時に、(メッセージングの──)OOPLといっても、“メソッド”と称したメンバー関数の動的呼び出しで実装するだけが唯一のやり方ではないのだよ!ということがよく分かります。

条件分岐式についてもう少しに続く)