Smalltalk-72で学ぶOOPの原点:まとめ

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

アドカレのかたちを借りて、アラン・ケイの“オブジェクト指向”というアイデアをもとに(非同期処理などいろいろ足りていないながらも──)比較的忠実に実装された1970年代の非常に古いSmalltalk-72でいろいろ遊んでみたわけですが、お楽しみ頂けましたでしょうか。

絵文字などが多用されているコードの見た目の時点でもうすでにかなりユニークなSmalltalk-72ですが、純粋に言語として見ても、今でも十分異端とされる我々の知るSmalltalk-80以降の現在に至る実装(例えばPharo)のそのまたさらに遥か斜め上を行く“メッセージング”の徹底具合とその実現方法に度肝を抜かれたかと思います。

今のSmalltalkを含めて、Smalltalk-76以降のSmalltalkがパフォーマンスを優先してそれを選択したが故に、現在主流のメッセージングのOOPLの実装は「メンバー関数を“メソッド”と呼び、その動的コール&風変わりな例外処理(メソッドがないときに発動)の組み合わせを“メッセージ”と称する」のが唯一の正解とされてしまいがちですが、実装のバリエーションはいろいろあってもっと試されるべきなんだよ!(「だったんだよ!」ではなく現在進行形なのも重要!)ということを少しでもお伝えできたならさいわいです。


以下、このシリーズを通じてSmalltalk-72で遊んで気がついたことをつらつらと。(順不同。逐次追加)

  • トークン列のパースをメッセージングに見立てたアイデアは斬新だが、今のSmalltalkのようにコンパイル言語にはしにくい(実行速度は稼ぎにくい)かも。
  • 文法を定義できるのは強力だが、コンテキストによってコードの意味が変わるのは読むときつらい(つーか、字面を追うだけでは無理)。
  • 動的にコードを読む(実行時の動きを追う)にしても、今のSmalltalkのような強力なデバッガーが無いのがつらい(モードレスエディタ同様に発明前なので当然だが──)。そもそも、現在のOOPLに至ってもなおこれ無しに動的言語のコーディングや既存コードの解析をしている人を尊敬する!
  • このSmalltalk-72に影響を受けたカール・ヒューイットのアクターの実装(PLASMA)がどうなっていたか調べてみたい。Erlang/Elixirのメッセージングの実装がそれにどの程度影響を受けたものか確かめたい。
  • いろいろ工夫はできるが継承がないのはやっぱりつらい。継承にするかはともかく、コード(あるいはコード片)をオブジェクト間で共有する何らかの機構は(ユーザーサイドのノウハウの蓄積に頼るのではなく──)処理系サイドで用意していただきたい!
  • このあと発明されたコピペを含むモードレスエディタは偉大!(LivelyWebをもうちょっと勉強すればペースト機能くらいは追加できるかも…)
  • 知らないメッセージが来ると、冒頭のトークンをアクティベートして新しいコンテキストで処理が始まるのは斬新。
  • ただ、今のSmalltalkのようにオブジェクトにメッセージを送る(正味は動的な関数コールなのはさておき──)とそれがトリガーになる感じのほうが好き。
  • なんかこう(今のSmalltalkにおいてオブジェクトがいつもそこにあって自律してるイメージではなく──)いちいち処理系がしゃしゃり出てくる感じ(?)がSmalltalk-72方式にはある。
  • あと、知らないメッセージを無視してしまうことになるので、フォールスルー処理でできることが限定される。これはよくない。→あ、これ、ピーク(鍵穴マーク)を使えばできるのか!
  • でも、ネットワークや細胞間での通信とのアナロジーからはスルーする方がよく合致する。ここらへんアラン・・ケイ的にどう考えているのか知りたい。
  • 記号は1文字でトークンになるところがミソであるが(Smalltalk-76も同じ。なお、-80以降の今のSmalltalkでは記号が続くとまとめてトークンになるので、<=>とか新しいメソッドを追加できる──)、「=<」「<=」の代わりに「≦」を用いる等で必然的に多くの記号が必要になり、ともするとコードの読み下しのしにくさ、またLivelyWeb版に関してはWebブラウザにキーアサインを奪われることでエミュレーターの制約につながってしまっている。
  • 代入(☞<変数名>←<値>)や(Smalltalk-80以降は別の方法で戻ったが──)条件分岐(<条件式>⇒(<非偽時処理>)<偽時処理>)も一貫して意味的にはちゃんとメッセージ式になっているのはさすが。
  • アクションやクラスが実質クロージャーなのがびっくり。
  • メソッドが独立した関数ではなく、巨大なIF式の中にネストされたひとつのパターンマッチのコード片に過ぎないのもびっくり。
  • アクションやクラスがアクティベートされる(トークン列からオブジェクトだと判断される)とすぐにコードが実行されてしまうのはわかりにくいし、第遺丘オブジェクトとして扱いにくい。
  • 処理系の細かな挙動を理解するにあたっては、Smalltalk-72のオリジナルの実装者であるダン・インガルス自身によるSqueak版Smalltalk-72の実装が(若干齟齬はあるものの──)非常に参考になる。ここでもやはり今のSmalltalkのデバッガーは必須のツールであった。

Smalltalk-72で学ぶOOPの原点:サンプルコード「じゃんけんゲーム」(@nrslibさんのSIMULA版を移植)

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


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

じゃんけんゲーム

以前、@nrslibさんとSmalltalk-72およびSIMULAの話をした際に、

その流れで即座に作られたSIMULA版(Cimを使用)のじゃんけんゲーム

こちらを比較のためSmalltalk-72に移植させていただきました。

インスタンス変数dispが同名のグローバル変数と被るのでこれをdisplayに変更した以外は、なるべく忠実に再現したつもりですが、ちょっとだけ余計なひねり(後述)を入れています。どうぞあしからず。なお、クラス名を大文字スタートにしていなかったり、変数名等を含めキャメルケースにしなかったのは、Smalltalk-72の他のコードの雰囲気に合わせただけで、言語の制約によるものではありません。いずれも普通に使えますので、お手元で試される際は読みやすいように適宜書き換えてください。

まず、jankenクラスとenglishdisplayおよびjapanesedisplayアクションです。これらの名前はそのまま~dispのままでよかったかと思いますが、先述のインスタンス変数等のdispdisplayに変更するのにあわせてうっかり変えてしまいました。

f:id:sumim:20191227205335p:plain
「janken」「englishdisplay」「japanesedisplay」の定義

Smalltalk-72にはそもそも継承機構が無いため、englishdisplayjapanesedisplayに共通の抽象クラスであるdisplayは定義していません。もしテンプレートメソッドパターンが使われていたら、このシリーズでも取り上げたクラス変数を使ったメソッド(正確にはアクション)の共有テクニックなどを活用しようかとは思っていたのですが、今回はその出番はありませんでした。

そこで“ひねり”を思いついて、この2つはクラス(実際に使うのはそのインスタンス)ではなく、アクション(インスタンス生成能を持たないクロージャーのようなもの)で実装してみました。クラスもそうですが、アクションは名前を書くだけで実行されてしまうので、その本体を変数に代入したり引数として渡す際には参照(#~)で渡さないといけなくなるなど、かえって面倒になったような気もしますがまあよしとしましょう。^^;

呼び出し用のjankengameアクションの定義と実行例はこちらです。

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

実行時に「1」というメッセージを送るとenglishdisplayが、それ以外ではjapanesedisplayが選ばれる挙動は(プログラムの起動方法、オプションをメッセージとして与える等の手段は別にして──)元のSIMULA版と同じです。

シリーズ通して読んでいただいたなら、特に説明は不要かと思いますが、いちいち読んでいられない! and/or 分かりにくいところ等あれば補足しますのでコメント欄やTwitterのメンション、DM等で気軽にリクエストください。

まとめへ続く)

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