Smalltalk-72で学ぶOOPの原点:サンプルコード「joe the box」(is、'sの追加)

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


サンプルコード「joe the box」(turtleにplace追加、squareとboxの定義)の続き)

is」メソッドの追加

今のままではjoejill、つまりboxクラスのインスタンスたちは、メッセージ「is …」に対してしかるべく振る舞えず、untypedということになってしまいます。なお、?は「~」で入力できます。

f:id:sumim:20191225193947p:plain
「is …」メッセージに正しく応答できないjoe

これにきちんと答えられるようにboxクラスにisメソッドを追加しましょう。Smalltalk-72操作マニュアルの Page 22 には、学習目的でISITを使わないちょっと長めの素朴な記述が紹介されています

f:id:sumim:20191225201055p:plain
ISITを使わない「is」メソッドの例

が、ここでは素直に既に解説済みのISITを使って簡単に済ませてしまいましょう。

f:id:sumim:20191225194608p:plain
「is …」メッセージに正しく応答できるようになったjoe

's」メソッドの追加

これまた今のままでは、せっかく位置も変更できるように用意されたインスタンス変数xyが使えません。「xメソッド」「yメソッド」といったアクセッサーを用意するのも手ですが、ここはもう少し柔軟に対応できる'sメソッドを定義することで対処します。

既にご紹介した'sメソッドのイディオム(⇑⦂ eval、もしくは⇑(:☞) eval)は、メッセージの続きのトークンもしくは配列を評価せずに取り込んで(、もしくは(:☞))からあらためて評価し(eval)その結果を返す()というものでした。

isメソッド同様、addtoアクションを使ってboxクラスに簡単に追加できます。

f:id:sumim:20191225205639p:plain
「⇑⦂ eval」として定義された「's」メソッドをaddtoアクションで追加する

ただしこの定義の場合、ゲッターとして使うときは欲しい変数を指定するだけで済むのですが──

f:id:sumim:20191225203051p:plain
「⇑⦂ eval」として定義された「's」メソッドを使ってjoeから「x」の値を得る

xに別の値を代入しようとすると、そのための式を括弧でくくって与える必要が生じます。

f:id:sumim:20191225203406p:plain
「⇑⦂ eval」として定義された「's」メソッドを使ってjoeの「x」に値を代入する

ここは「joe's x ← 64」などとスマートに書きたいところですが、⇑⦂ evalのままでは「joe's x」で式が完結し、その返り値である数値「127」に改めて「← 64」が送られることになるため、結果、エラーが発生してしまいます(数値は← …メッセージに応答できないため。仮に応答できても望んだ結果にはなりませんが…)。

f:id:sumim:20191225204243p:plain
「⇑⦂ eval」として定義された「's」メソッドでは「joe's x ← 64」はエラーになる

そこで、あらためてSmalltalk-72操作マニュアルの Page 22 に紹介されている'sメソッドを見てみると──

f:id:sumim:20191225205856p:plain
Smalltalk-72操作マニュアル Page 22 の「's」メソッドの定義

取り込んだトークンをいったん一時変数「var」に代入し(☞var←⦂.)、続くメッセージが「」なら(∢ ←⇒(…))その後に続くメッセージを評価して取り込んでから「var」に入った変数に代入して値をリターン(⇑var←:)、そうでなければ改めて取り込んだトークンを評価(変数ならその値を得る)して結果をリターンする(⇑var eval)という、ちょっと凝った処理をしています。この定義に差し替えて期待通り振る舞うか試してみましょう。

すでに「's」メソッドを定義済みなので、与えたコード片を単純にコード末尾に追加するだけの「addto」アクションは使えません。そこで「edit」アクションでエディターを起動し、Replaceで「's」メソッドを差し替えます。

f:id:sumim:20191225210438p:plain
「edit box」でエディターを起動する

f:id:sumim:20191225210520p:plain
Replace→「∢'s ⇒ ()」の「()」を二度クリックして選択→差し替えるコード片を入力してdo-it(「\」キー)

Exitでエディターを抜けて元のREPLに戻れば、今度は「joe's x ← 64」が期待通り動作するようになっているはずです。

f:id:sumim:20191225210822p:plain
差し替えた「's」メソッドでは「joe's x ← 64」が期待通り動作する

こんな記述も可能になります。

f:id:sumim:20191225212711p:plain
joeの「x」をjillの「x」から100を引いた値にする

's」はRubyのinstance_eval相当なのですが、とても自然に書き下せていますね。このように文法を気軽に拡張できることは、たいへん柔軟でパワフルである反面、なんとも怖ろしくもあります(そしてそれが次の世代のSmalltalk-76が同様の実装にならなかった理由のひとつであり、また、LISPの強い影響下にありながらRubyにマクロがない理由のひとつでもあるわけです)。

Smalltalk-72操作マニュアルには、この他にmoveメソッドの追加や、そのバリエーション、redrawメソッドを使った別のboxの実装なども紹介されているので是非試してみてください。

サンプルコード「ローンシミュレーション」(report、demand、payment)に続く)