Smalltalk-72で学ぶOOPの原点:サンプルコード「ローンシミュレーション」(report、demand、payment)

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


サンプルコード「joe the box」(is、'sの追加)の続き)

ローンシミュレーション

「Open the ST-72 Manual」でダウンロードして表示することができるSmalltalk-72操作マニュアルのPage 84からにあるローン返済シミュレーションを書いてみます。

元コードではfloatクラスに$メソッドを追加して小数点以下を丸める操作ができるようにしているのですが、あいにくLivelyWeb版のSmalltalk-72(と、それに手を加えたちょい直し版)では「$」のグリフを持つ記号を入力できないので、代わりにdollarメソッドとして追加することにします。既存クラスへのメソッド(パターンマッチのコード片)の追加にはaddtoアクションを使います。

f:id:sumim:20191227195000p:plain
「float」クラスへの「dollar」メソッドの追加と動作確認

show floatでコード片が最後に追加されているのを実際に見ることができます。

f:id:sumim:20191227195152p:plain
「dollar」メソッド(パターンマッチのコード片)が追加された「float」クラス

打ち間違いなどあってうまく動かないときはedit floatで組み込みのエディタを起動しそこで修正してください。なお、組み込みエディタは前述の“ちょい直し版”でのみ操作可能です。ご注意あれかし。

次にreportdemandというアクションを定義します。これらはそれぞれキャプション付きの出力と入力機能を提供します。

f:id:sumim:20191227200531p:plain
キャプション付きの出力機能を提供する「report」アクション

demandは、as <文字列>とメッセージに続けると指定した文字列をキャプションにし、これを省略すると代入する変数名がキャプションとして表示され、直後にアルトを模したアイコンがプロンプトとして表示されるので値を入力してdo-it(「\」キー)すると値が入力されます

f:id:sumim:20191227200617p:plain
キャプション付きの入力機能を提供する「demand」アクション

最後に本体のpaymentアクションを定義すれば完了です。あいかわらず「$」マークが入力できないので、キャプションで「$」を表示するために、いったん一時変数dollarに長さ1文字の文字列を代入し(☞dollar ← string 1.)、それを「$」マークの割り振られたコード27に置き換えておき(dollar[1] ← 20)、適宜キャプションの最後に追加(+ dollar)というようなことをしています。

定義後にpamentと入力してdo-it(「\」キー)すると、借入額、年利、返済年数、年当たりの返済回数を尋ねられるので入力すると結果が出力されます。

f:id:sumim:20191227202159p:plain
「payment」アクションの定義と実行例

サンプルコード「じゃんけんゲーム」(@nrslibさんのSIMULA版を移植)に続く)

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)に続く)

Smalltalk-72で学ぶOOPの原点:サンプルコード「joe the box」(turtleにplace追加、squareとboxの定義)

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


ウインドウを無理矢理サイズ変更&移動するの続き)

「joe the box」とは

Smalltalk-72の操作マニュアルや関連の文献に良く出てくる定番のデモです。四角を描くクラス「box」を定義したり、それをインスタンス化して「joe」とか「jill」といった名前を付け、彼・彼女らにメッセージを送ってインタラクティブに操作することを通じ、メッセージングのオブジェクト指向を学びます。

turtleクラスにplaceメソッドを追加

show tuttleでtuttleクラスのコードを見てみましょう。前回ご紹介した方法(disp's (☞winht←☞frmht←666. ☞buf←buf+string 3000). disp put '' at 16 16. disp display. disp clear など)で、ウインドウを画面一杯に広げておけばぎりぎり表示が収まります。'sの入力にはちょい直し版が必要です。'sはctrl-shift-sか「|」で、(指さし矢印)は「"」で入力できます。

turtleクラスの定義にplaceに応答するためのメソッドが書かれていないことを確認したら、addtoアクションを使ってこれを追加しておきます。

f:id:sumim:20191224225054p:plain
turtleクラスの定義(「show tuttle」で表示)と、addtoアクションを使ったplaceメソッドの追加

(アイボール)は「%」、(~ならば)は「?」、(リターン)は「!」、(do-it)は「\」で入力できます。

squareアクションの定義

squareアクションは、boxクラスのインスタンスが四角を描くのに使います。数値をメッセージとして送ることで、一辺の長さが決められます。描かれる四角の向きは、タートル(😄)の向きに準じます。

f:id:sumim:20191224230943p:plain
squareアクションの定義

(代入)は「_」、😄(スマイリー)は「@」で入力できます。

boxクラスとjoeおよびjill

boxクラスは、位置を保持するxy、描く四角の一辺の長さのsize、角度のtiltというインスタンス変数を持ち、ここで示す定義の段階ではdrawundrawgrow <長さ>turn <角度>というメッセージに応答できます。

boxクラスをコールしてインスタンスを生成させjoeもしくはjillに代入すると、画面に四角が描かれます。

grow <長さ>turn <角度> といったメッセージを送って、どのような変化を見せるか実際に試して楽しんでみてください。

f:id:sumim:20191224231743p:plain
boxクラスの定義とjoeおよびjillへのインスタンスの代入、メッセージングによる操作

ウインドウを大きくしてしまった影響で、スクロールが発生するとjoeやjillが消えてしまうので、ちょっと手間ですが、定義が終わって遊ぶ前にウインドウを元の小さなサイズにもどしておく(disp put '' at 16 514. disp's (☞winht ← ☞frmht ← 168). 😄 erase. disp display. disp clear)とよいかもしれません。

サンプルコード「joe the box」(is、'sの追加)に続く)

Rubyのブロック引数宣言の“ | ”(バーティカルバー)のルーツをSmalltalk-72に探す

アラン・ケイの“オブジェクト指向”というアイデアをもとに(非同期処理などいろいろ足りていないながらも──)比較的忠実に実装された1970年代の非常に古いSmalltalk-72で遊んでみるシリーズ(記事一覧→Smalltalk-72で学ぶOOPの原点 Advent Calendar 2019 - Qiita)ですが、今回はコラムとしてRubyのブロック変数の宣言に用いられる | のルーツを探ります。


Rubyのブロックと引数宣言

Rubyにはブロック付きメソッド呼び出しのための構文があって、メソッド呼び出しの末尾に{ }で括った処理の記述(ブロック)を付すことで、その処理を引数(オブジェクト)としてメソッドに渡すことができます。ブロックは0個以上の引数をとることでき、その冒頭で| |で括って仮引数(ブロック引数)をカンマで区切るなどして列挙することで宣言できます。

[1,2,3].collect{ |a| a * 2 } #=> [2,4,6]

このブロック引数をくくる| |という奇妙な記法がSmalltalk-80(以降の今のSmalltalk)を模倣により生まれた経緯については「まつもとゆきひろ 言語のしくみ」の「1-5 言語デザイン入門(後編) ブロック」に書かれているとおりですが、ではこの「|」はどこから来たのか?というのが今回のお話です。

Smalltalk-72のクラス定義における「|

LivelyWeb版のSmalltalk-72エミュレーター(より正確には、LivelyWebでALTO/Novaを部分的にエミュレートし、そこに当時保存されたSmalltalk-72が動いている状態のメモリダンプを読み込ませることで再現された処理系。St-72アドカレ後半で使ったsumimによるちょい直し版はこちら)の画面にある「Open The ST-72 Manual」ボタンを押すとダウンロードして読むことができる1976年刊のSmalltalk-72の操作マニュアルには次のように、変数の種類(一時変数、インスタンス変数、クラス変数の宣言)の区切りには「|」を使うことが示されています。

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

しかし、実はこれに従って記述しようとしてもLivelyWeb版Smalltalk-72ではクラスを定義することができません(そもそもグリフが「|」である記号を入力できないので…^^;)。「ALLDEFS」という別のボタンをクリックして呼び出すことができる1974年頃に使われていたブートストラップコードを参照すると、なるほど、1976年より前のある時点では区切りには「|」ではなく「:」を使っていたことが分かります。

f:id:sumim:20191223170430p:plain
ALLDEFSにある「turtle」クラスの定義

1974年の実装から1976年に刊行された操作マニュアルが対象にした処理系に至るどこかの時点で|が使われるようになり、それがSmalltalk-80以降の一時変数宣言に使われた「|」 、ひいてはRubyのブロック引数をくくる「|」につながったというわけです。

「区切り」か「くくり」か?

1976年刊の操作マニュアルが対象にしているSmalltalk-72処理系のクラス定義にに使われた「|」は、あくまで「区切り」としてでしたが、Smalltalk-80以降の今のSmalltalk(一時変数)やRuby(ブロック引数)では「くくり」として使われています。

Smalltalk-80を第三世代、Smalltalk-72を第一世代とすると、その間に開発された第二世代のSmalltalk-76ではまだ「|」は「区切り」として使われていました。ただし、Smalltalk-72のように変数の種類を区切るためではなく、メソッド定義においてメッセージ記述を模した「メッセージパターン」(メソッド名と仮引数の宣言記述を兼ねる)と一時変数の宣言の区画の区切りとして…でした。

f:id:sumim:20191223174043p:plain
Smalltal-76のArrayクラスのメソッド「all←」の定義(「all←val」がメッセージパターン、うち「val」が仮引数宣言。「i」が一時変数宣言。[…]内がメソッドの記述)

これは想像ですが、この後のSmalltalk-80以降の今のSmalltalkでは、ブロック(Rubyとは違い、単独でリテラル的な記述が可能な第一級のオブジェクト)の導入に伴ってメソッド本体を[ ]でくくる記法(<メッセージパターン> | <一時変数宣言> [ <メソッド処理> ])をやめにしたため、一時変数との区切りとしての「|」がひとつでは足りなくなり、メソッド本体との間にも「|」を入れる(<メッセージパターン> | <一時変数宣言> | <メソッド本体>)ことで結果的に「|」でくくる記法が成立したのではないでしょうか。

ともあれ、Smalltalk-80以降の「|」でくくるのがRubyのブロック引数をくくっている「|」のルーツでFA…かというとそうではなく、話はもう少し複雑なようです。

Smalltalk-80以降のブロック引数宣言

先述の「まつもとゆきひろ 言語のしくみ」にはもう答が書かれているのですが、当初Rubyのブロック引数宣言の「|」は当該宣言部分とブロック処理記述部分の区切りとしての意味を持っていたのが、その後廃止されて今の「くくり」になったのだそうです。

f:id:sumim:20191223174951p:plain
まつもとゆきひろ 言語のしくみ」より

Smalltalk-80以降の今のSmalltalkのブロック変数宣言は次のように書きます。

#(1 2 3) collect: [:a | a * 2] "=> #(2 4 6) "

これも想像の域を出ないのですが、Smalltalk-80以降のブロックでは、メソッドにおいて「|」が「くくり」として意味が変わっていく中にあっても相変わらず「|」を区切りとして用い続けていて(これは今のSmalltalkに至るまで「くくり」に変わることはなかった…)、それを模したRubyが今度はSmalltalkのメソッドにおける「|」とは経緯や理由は違うものの結果的には同じ収斂進化を経て「くくり」に落ち着いた…というのが正解に近いような気がします。

おまけ:Smalltalk-80のブロック引数の前の「:」について

こちらについては、Lispのキーワードを模したものかと当初思っていたのですが、さにあらず。メソッドにおける仮引数宣言を兼ねたメッセージパターンと同じものをブロック記述にも応用するにあたり、ブロックにはメソッドのメソッド名に当たる「セレクタ-」およびそれを構成する「キーワード」が無いので、コロンだけを残し、:a のような記法になったというのが正解でした。

ただ、Smalltalk-72を見ていると、メッセージ中の続くトークンを評価して変数に取り込む「:<変数名>」というイディオム(「:」というアクション=オブジェクト に対する<変数名>メッセージの送信式)があり、これも引数を変数に代入するという似たような意味を持つので、まったくの無関係ではないような気もします。

以上、Rubyファンはおろか、今のSmalltalkファンにとってもけっこうどうでもいいマニアックな話でした。^^;

Smalltalk-72で学ぶOOPの原点:ウインドウを無理矢理サイズ変更&移動する

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


ユーザー定義クラス・アクションの一覧「defs」と定義の出力「show」の続き)

対話ウインドウクラスdispframeとそのインスタンスdisp

画面の下の方に表示されている矩形領域はdispframeという対話ウインドウクラスのインスタンスで、使用中のものはdispという変数に代入されています。

f:id:sumim:20191220000355p:plain
「disp」に代入された「dispframe」のインスタンス

例によってALLDEFSでto dispframe …で始まるコードやコメントを読むとこのウインドウの使い方(応答できるメッセージやその送り方)が分かります。

たとえば、すでに何度か出てきているように← <ASCIIコード>というメッセージを送れば指定したASCIIコードに割り振られた文字が、← <文字列>というメッセージを送ればその文字列が表示される…とか、

f:id:sumim:20191220000833p:plain
「dispframe」クラスに定義されたメッセージ「←」への応答(コメントにて)

f:id:sumim:20191220001002p:plain
「← <文字コード>」、「← <文字列>」への応答

画面が汚れてしまったらdisp clearで消して、disp displayで描き直すことができる…とか、です。

f:id:sumim:20191220001312p:plain
ウインドウ領域を消去するには「disp clear」

f:id:sumim:20191220001422p:plain
消去したあと、改めてウインドウを描き直すには「disp display」

一方で、「Open the ST-72 Manual」ボタンでダウンロードして読むことができる1976年刊の「Smalltalk-72 Instruction Manual」の記述にはある、ウインドウを移動したり(moveto)、サイズを変える(growto)といったいくつかのメッセージ

f:id:sumim:20191220001712p:plain
ここにある例のうち「hide」「moveto」「growto」は定義されておらずエラーになる

これらへの応答は、「ALLDEFS」ボタンで参照できる1974年のALLDEFSには(かつ、それに近いバージョンのALLDEFSで起動しているらしいこの処理系のメモリダンプにも)まだ実装されていないことも分かります。

また、マニュアルにある1976年ごろのバージョンには、新規ウインドウの作成、削除(閉じる)、大きさの変更、移動といったマウス操作も可能だったようですが、それらも当然まだ実装されていません。

f:id:sumim:20191220011517p:plain
1976年頃のウインドウ操作に関する記述(Smaltalk-72 Instruction Manual, pp 5-6)※このエミュレーターでは使えません

これらは矩形領域の高速な描画を可能とするBitBLTルーチンが発明され、あるいはそれがマイクロコード化された後に実装された機能だと思われます。

f:id:sumim:20191220012807p:plain
BitBLTルーチンへのアクセスに関する記述(Smalltalk-72 Instruction Manual, pp 42-43)※このエミュレーターでは使えません

put <文字列> at <x座標> <y座標>

ウインドウ操作に関連する振る舞いが軒並み未実装ななか、かろうじて、任意の文字列を任意の場所に表示するdisp put <文字列> at <x座標> <y座標>という式は使えるようです。

f:id:sumim:20191220002532p:plain
メッセージ「put <文字列> at <x座標> <y座標>」への応答を記述したコード

これは実質、ウインドウを移動しているコードなので、このメッセージを送ることでdispを画面の上の方へ移動してみましょう。ただしこのput …メッセージは、レシーバーであるdispframeインスタンス(ここではdisp)を移動しはするものの、指定した文字列を表示するだけでフレームの描き直しなどはしてくれません。そこで「disp display. disp clear」を続けて実行することで、枠やプロンプトの表示も行います。

f:id:sumim:20191220003349p:plain
「disp '' at 16 16」でウインドウを画面上部に移動し、「disp display. disp clear」で枠を描き直した結果

dispに手をつっこんで('s)縦の長さ(winhtfrmht)を変える

dispframeインスタンス生成式やそれについてのコメント、

f:id:sumim:20191220004512p:plain
「dispframe」のインスタンス生成式やインスタンス変数についてのコメント

あるいはdispframeインスタンス化時(つまりdispframeが直接コールされた結果、isnewが非偽値を返すときに実行される──)isnew ⇒(…以降の初期化コード片を読むと、ウインドウの高さはwinhtfrmhtに保持されていることが分かります。

f:id:sumim:20191220003909p:plain
「dispframe」のインスタンス生成時(「isnew」が非偽時)の初期化処理を記述したコード片

そこで、disp's (<評価したい式>)を送って、直接これらのパラメーターを書き換えてしまいます。より多くの行数を表示できるように、バッファー(buf)も増やしておきます。「disp display. disp clear」の追加も忘れずに。

f:id:sumim:20191220004906p:plain
「disp」の高さを無理矢理変えて描き直すと…

f:id:sumim:20191220005057p:plain
画面いっぱいに広がったウインドウ

任意の場所にスクロールさせることはできませんが、このくらいの縦幅があれば、クラスやアクションの定義を整形して表示するshowアクションもかなり快適に使えます。

f:id:sumim:20191220005438p:plain
「show obset」をdo-it(\キー)してコード全体を表示させてみた様子

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

Smalltalk-72で学ぶOOPの原点:ユーザー定義クラス・アクションの一覧「defs」と定義の出力「show」

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


Rubyのinstance_eval相当の「's」の続き)

defsによる、ユーザー定義クラス・アクションの一覧

defsには、ユーザーが定義したクラスやアクションが収められています。

f:id:sumim:20191219013210p:plain
クラスやアクションをユーザーが定義すると「defs」に追加される

defsobsetというクラスのインスタンスです。

f:id:sumim:20191219013342p:plain
「defs」は「obset」のインスタンス

obsetについて

obsetは、今のSmalltalkBag(重複を許す集合)とSet(重複を許さない集合)のどちらとしても使えるSmalltalk-72組み込み(ブートストラップ時=ALLDEFSに定義)のコンテナクラスです。

f:id:sumim:20191219010729p:plain
「obset」の定義

定義を見るとわかるように、add <要素>メッセージで追加した場合は要素の重複を許し(つまり、すでに同じ要素があっても追加される)、← <要素>メッセージで追加した場合は既に追加済みなら無視される仕組みになっています。

f:id:sumim:20191219011632p:plain
「obset」をBagのように、もしくは、Setのように使う

アクションやクラスの定義の出力

showアクションにクラスやアクションを送ると、その定義を整形して出力することができます。

f:id:sumim:20191219012213p:plain
「show」アクションでクラスの定義を整形して出力

例によってALLDEFSのto show …から始まる記述付近に定義があるので余力があれば参照してみてください。

まさにこのようにALLDEFSから該当するコードを探して読んだり(組み込みクラス・アクションの場合)、editで配列(ブロック)にいちいち潜りながら読むよりずっと便利な機能なのですが、縦に長くなりがちな整形したコードを出力するにはデフォルトのウインドウの縦幅が狭すぎます。ちょっと長めのコードをshowで表示させると、すぐ画面からあふれて使い勝手が悪いです。これを少しでも解消するため、ウインドウを移動してもっと大きく全画面表示にできないか考えてみましょう。

ウインドウを無理矢理サイズ変更&移動するに続く)

Smalltalk-72で学ぶOOPの原点:Rubyのinstance_eval相当の「's」

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


組み込みの構造化エディター(ソース概説))の続き)

'sでオブジェクトの中身を直接いじる

すでに何度か触れたり、エディターのソースにも出てきた'sは、それに続くメッセージとして送られる「括弧でくくられたコード(ある意味で“ブロック”)」をレシーバーのコンテキストで実行する振る舞いをさせるときに使う(ことになっている)記号です。

ただ、これもやはり既に述べたとおり、継承の無いSmalltalk-72ではObjectに代表される基底クラスに定義された“デフォルトの振る舞い”などというものは期待できないので、自作のクラス(に限らず、それが期待される全てのクラス)にはそうした振る舞いをさせたい旨の記述をいちいちしておく必要があります。

試しに、インスタンス変数ivを持つクラスfooについて、通常どおりiv ← <値>というメッセージを送るのとは別に、'sで直接値を変更することも可能なように定義してみましょう。

インスタンス変数はクラス名の後に(一時変数宣言と区別するために──):(コロン)で区切りを入れてから宣言します。アクセッサーのパターンマッチコード片は「ivというメッセージを受け取ったら(∢iv⇒()、続くメッセージがなら(∢←()、さらにそれに続くメッセージを評価してivに代入してからその値を返し(⇑:iv))、そうでなければ単純にivを返す(⇑iv))」と記述します。

一方、'sのパターンマッチコード片はALLDEFSに何度も現れる決まり文句ならぬ“お決まりのコード片”のとおり「∢'s⇒(⇑⦂ eval)」です。は続くメッセージとして与えられたトークン(配列ならトークン列)を評価せずにトークンのまま取り込むアクションで、したがって⇑⦂ evalは取り込んだ配列を自分のコンテキストで評価(eval)して返す()ことを意味します。

'sという一文字の記号は、ちょい直し版でのみ入力可能で、「KeyboardHelp」にあるとおり ctrl+shift+s か、あるいはブラウザなどの都合で同キーアサインが使用できない場合は「|」をタイプすることでも入力できます(ちょい直し版は、ドキュメント通りの ctrl+shift+s が機能するようにすると同時に、代替キーアサインの追加もしています)。

f:id:sumim:20191217224538p:plain
インスタンス変数「iv」を持ち、そのアクセッサーとは別に「's」に応答できるクラス「foo」の定義

xfooインスタンスを代入し、アクセッサーの動作を確認します。

f:id:sumim:20191217225859p:plain
クラス「foo」のインスタンス作成とアクセッサーの動作確認

この状態でインスタンス変数ivには3が代入されているわけですが、これを'sを使って直接4に書き換えてから、あらためてアクセスしてみましょう。

f:id:sumim:20191217230852p:plain
「's」を使って直接インスタンス変数を書き換える

'sが入力できないときの代替策

'sが入力できなくても、たとえばssなど適当なメッセージに応答して同じ振る舞いをするように記述すればよいわけですが、ただ、が入力できないと件のお決まりのコード片である「∢ss⇒(⇑⦂ eval)」が記述できずそこで困ってしまいます。改めてアクションの定義をALLDEFSで探すと次のように(プリミティブCODE 36を呼んでこそいますが──)「(:☞)と等価である」と分かります。

f:id:sumim:20191217231911p:plain
「⦂」アクションの定義

したがって、'sの代替をを使わずに書くと∢ss ⇒(⇑(:☞) eval)と書くことができ、既存のクラスにもaddtoアクションを使うか、editの「Add」等で追加できます。

f:id:sumim:20191217232642p:plain
「's」と「⦂」が入力できないときの回避策

それでも'sが入力できないと、そのうち不可能なこともなにかと出てきて不便なので、なんとか'sを入力できるようにした件の「ちょい直し版」を作ったというわけです。

ユーザー定義クラス・アクションの一覧「defs」と定義の出力「show」に続く)