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

Smalltalk-72で学ぶOOPの原点:組み込みの構造化エディター(ソース概説)

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


組み込みの構造化エディター(定義済みアクションやクラスの編集)の続き)

“The Smalltalk(-72) Editor”

edit(やfixアクションでも同じく)で起動できるSmalltalk-72に組み込みの構造化エディター(ブートストラップ時=ALLDEFSに定義される──)は、いくつかの基本的なAPIコールこそしていますが、全体としてわずか100行ちょっとで実装されています。Smalltalk-72の記述力、恐るべし…ですね。もっとも、そのぶん難読ではありますが…^^;(それでも、個人的印象ではもっとずっと記述力は高いものの、かなり気合いを入れないと読むことができないAPL/Jはもとより、頭の中でリスト処理をしないといけないLisp族よりは幾分読みやすいかな…と)。

ソースコードをざっと眺めてみましょう。

http://squab.no-ip.com/collab/uploads/st72editor-overview.png
The Smalltalk(-72) Editorのソースコード

Rubyのinstance_eval相当の「's」に続く)

Smalltalk-72で学ぶOOPの原点:組み込みの構造化エディター(定義済みアクションやクラスの編集)

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


組み込みの構造化エディター(Add、Insert、Replace、Delete、Move、Up、Push)の続き)

addtoアクションで定義済みアクションやクラスにコード片を追加する

たとえばto foo (isnew)のように、最小限の定義しかしていないクラスfooがあったとして、これだとfooインスタンスはクラスを問い合わせるis?というメッセージには答えられない(正確には、スルーされたメッセージの先頭のisがアクションとしてアクティベートされ、それが続く?メッセージを受け取ってデフォルト値であるかのように「untyped」を返す偽装をしている)という話はすでにしました

f:id:sumim:20191215230946p:plain
最少構成のクラスに属するオブジェクトはクラスの問い合わせに正しく応答できない

このクラスfooインスタンス(上の例ではxに代入)に正しく自分が属するクラスを返させるためには、fooのメソッドに∢is ⇒(ISIT eval) というパターンマッチを追加すればよいわけですが、これはaddtoアクションを使うことで簡単にできます。

f:id:sumim:20191215231848p:plain
「addto」でクラス「foo」にパターンマッチを追加

コードを( )で括って与えるtoアクションと違い、addtoアクションで追加したいコード片は配列リテラル(頭にを追加。余談ですが、より厳密にはこれもというアクションへのメッセージ送信で実現されている──)なので注意してください。

本格的にクラスやアクションのコードを編集するならeditアクション

REPLのfixと同じように、構造化エディターを使ってクラスやアクションのコードも編集できます。

f:id:sumim:20191215232835p:plain
「edit」アクションにコードを編集したいクラスをメッセージとして送ると…

f:id:sumim:20191215232940p:plain
構造化エディターが起動し、「fix」と同様にコードを編集できる

使い方は前回紹介したfixのそれとまったく同じです。

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

Smalltalk-72で学ぶOOPの原点:組み込みの構造化エディター(Add、Insert、Replace、Delete、Move、Up、Push)

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


組み込みの構造化エディター(Enter、Leave、Exit)の続き)

「Add」「Insert」「Replace」「Delete」「Move」

「Add」は最後に要素を追加するときに使います。「Add」をクリックするか、編集中の要素のどれかを2度クリックすると(うまくマウスクリックを受け付けると)「Add」が反転表示になり、プロンプト(アルトを模したアイコン)が現れます。

f:id:sumim:20191214234635p:plain
「Add」モード

この状態で追加したい要素をタイプして入力しdo-it(\キー)を押すと記入した要素を最後に追加されます。

f:id:sumim:20191214235024p:plain
追加したい「+ 6」をタイプして入力してdo-it(\キー)

f:id:sumim:20191214235213p:plain
「+ 6」が追加される

「Insert」は指定した位置への要素の挿入です。「Insert」を押して反転させた状態で、要素を挿入したい場所の要素をクリックして指定すると「Add」と同様にプロンプトが現れるので挿入したい要素をタイプして入力し、do-it(\キー)します。

f:id:sumim:20191214235643p:plain
「Insert」→クリックで挿入位置指定→挿入したい要素をタイプして入力→do-it(「\」キー)

f:id:sumim:20191214235912p:plain
指定した場所に「+ 5」が挿入される

「Replace」は指定した範囲の要素の入れ替えです。「Replace」を押して反転させた状態で、入れ替えたい範囲の最初の要素をクリック(要素の上半分が白黒反転)したあと、続けて入れ替えたい範囲の最後の要素をクリック(要素の下半分が白黒反転)してから、プロンプトで入れ替える要素を入力してdo-it(\キー)します。入れ替える要素はひとつでも構わなくて、その場合は同じ要素を二度クリックします。

f:id:sumim:20191215001039p:plain
「Replace」→範囲を二度クリックして指定→入れ替えたい要素を入力してdo-it(「\」キー)

f:id:sumim:20191215001254p:plain
「+ 5」が「* 7」に置き換わる

「Delete」は指定した範囲の要素の削除です。「Delete」を押して反転させた状態で、削除したい範囲の最初の要素をクリック(要素の上半分が白黒反転)、続けて最後の要素をクリック(下半分が白黒反転)すると、指定された範囲の要素が削除されます。

f:id:sumim:20191215001652p:plain
「Delete」→削除したい範囲をクリックして指定

f:id:sumim:20191215001807p:plain
「+ 6」が削除される

「Move」は指定した範囲の要素を任意の位置に移動します。

f:id:sumim:20191215002128p:plain
「Move」→動かしたい範囲を指定(上半分、下半分が白黒反転)→移動先をクリック

f:id:sumim:20191215002313p:plain
「+」の位置に「* 7」が移動

「Push」と「Up」

「Push」と「Up」は、括弧でくくる(新しい配列にする)のと、括弧(ネストした配列)から取り出す操作です。

「Push」を選択して範囲を指定すると、指定した範囲の要素がくくられて表示は() になります。「Enter」して要素を確認したり「Leave」で元に表示に踊れます。

f:id:sumim:20191215003018p:plain
「Push」→範囲を指定すると要素がくくられる

f:id:sumim:20191215003143p:plain
くくられた要素群は「()」表示に変わる

「Up」はネストした配列の中身を取り出す操作です。ネストした配列がひとつしか無い場合は「Up」を押すと直ちに展開されます。複数ある場合は「Up」を反転させた状態で展開したい配列を選びます。

f:id:sumim:20191215005406p:plain
ネストした配列がひとつしかないときは「Up」をクリックするとただちにそれが展開される

f:id:sumim:20191215005242p:plain
くくられていた「3 * 7」が展開される

「Exit」で編集を終了し、編集後の式が評価されます。ちなみにSmalltalk-72の演算は右結合なので3 * 7 + 43 * (7 + 4)となり、その結果の33とその後に改行(disp ← 13.)が出力されます。

f:id:sumim:20191215005804p:plain
「Exit」で編集を終了し評価結果が返される

f:id:sumim:20191215005921p:plain
結果の「33」と改行(disp ← 13.)が出力される

組み込みの構造化エディター(定義済みアクションやクラスの編集)に続く)

Smalltalk-72で学ぶOOPの原点:組み込みの構造化エディター(Enter、Leave、Exit)

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


念願のクリック、's(instance_evalで使う記号)、⦂(オープンコロン)を手に入れた!の続き)

REPLの便利機能(redofix)とコード/配列エディター

Smalltalk-72のREPLでは、redoで直前に評価した式を、またredo <正数>と数値を付すことで指定した数だけ前に評価した式の再評価を行えます。

f:id:sumim:20191213125038p:plain
直前に評価した式を「redo

f:id:sumim:20191213125108p:plain
直前の評価結果に「redo」による再評価結果が追記される

一方、fixは指定した式の一部を変更して評価できます。このとき、コード(実体は配列)専用のエディターが起動します。redo 同様にどのくらい遡るかも指定できます。

f:id:sumim:20191213125609p:plain
「fix」で直前に評価した式の一部を変更して再評価する

f:id:sumim:20191213125656p:plain
組み込みのコード/配列エディターが起動

このコード/配列エディターがマウスクリック必須なので、これまでfixやアクション/クラス編集用のeditなどが使用できずたいへん不便でしたが、今後はだいぶREPLの使い勝手も改善されそうです。ただし前回紹介したちょい直し版を使っていない場合、残念ながらこのエディターでの操作はできません。escキーを押してそのまま終了してください。

「Enter」「Leave」「Exit」

このエディターは配列の要素の編集が基本的な機能です。要素内に配列があるときは()トークンに分断されないよう、実際は1文字の記号)と省略されて表示され、編集中はひとつの要素として扱われます。

配列(もしくはコード)の中にネストした配列がひとつしかないときは「Enter」コマンドをクリックするとその中身を見ることができます。複数あるときは、「Enter」コマンドがハイライトした状態で中身を見たい配列(()で表示されている)をクリックします。

f:id:sumim:20191213150459p:plain
「Enter」でネストした配列の内容を表示

元の表示に戻るには「Leave」を使います。戻る画面がないときはエディターを抜けて編集中のコードを再評価します。「Exit」はどの階層からでもエディターを直ちに抜けてやはり編集中の式を再評価します。

組み込みの構造化エディター(Add、Insert、Replace、Delete、Move、Up、Push)へ続く)

Smalltalk-72で学ぶOOPの原点:念願のクリック、's(Rubyのinstance_evalライクに使う記号)、⦂(オープンコロン)を手に入れた!

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


知らないメッセージはスルーする?の続き)

LivelyWeb版Smalltalk-72処理系のいくつかの不具合を解消してみた

1970年当時のバイナリーがそのまま動いているという点で、Webアプリ and/or 限定的なALTOエミュレーターであるがゆえの制約は少なからずあるものの、体験や学習に最適!とご紹介している件の処理系ですが、実は長らく解決できていなかった、しかしどうしても解消したい問題がいくつかありました。

ひとつはマウスクリックのイベントが拾えないこと。これが使えないと、マウスによる描画等のスクリプトが動かせないという残念さはありますが、それよりなにより開発環境として、便利な組み込みの構造化エディターが使えない(編集対象のトークンやメニューコマンドの選択のためのクリックに反応しないため──)という点で大きな障害になります。もちろん、構造化エディター無しでもコードの編集はできなくはないのですが、イントロスペクションを駆使したかなりの力業で、ちょっと気軽に触れて遊んでみたいだけの体験者には向きません。

二つ目はRubyinstance_evalに相当する機能を使うときの 's というグリフの文字、および、メッセージから評価せずにトークンを1つだけ取り込むアクションに使う(オープンコロン)が入力できないこと。これらはSmalltalk-72のコーディングには必須といってもよい記号文字なのですが、いろいろ問題があり入力できませんでした。前者はssなどの新しいメソッドとして追加登録(addtoアクションを利用)する、後者は:☞で同じことができるので代替するというワークラアラウンドは見つけてはいたのですが、それなりに面倒でした。

そんなわけで、ユーザーが自由に改変して保存もできるLivelyWebならではの特徴を活かし、このたび思い切ってこれらの不具合を解消する細工を処理系に入れてみました。本家を置き換える自信はなかったので、ユーザーフォルダー(ディレクトリー)のsumimALTO-Smalltalk-72.tweak.htmlでアクセスできるようにワールドの複製を作りました。

Chrome推奨です。以降は、このワールドをベースに進めていこうと思います。

追記:その後、本家 https://lively-web.org/users/Dan/ALTO-Smalltalk-72.html もいろいろ改良され、私も思いきって修正を入れてみたので、マウスクリックの処理、's の入力は本家でほぼ問題なく扱えます。Snippets ウインドウからの二回クリック(ダブルクリックにならないように、ゆっくり、二回)による、コードの転送などにも対応していて便利なのでぜひ本家をお使いください。なお、残念ながら Smalltalk Zoo からリンクされているインスタンスは権限が解放されておらず、修正を加えて保存することができませんでしたので、マウスクリックに関してはこちらは使用できないままです。ご注意ください。

追記 [2021-09-08]:残念ながら本家 https://lively-web.org/users/Dan/ALTO-Smalltalk-72.html が削除されて使えなくなってしまったようですので、当面は「LivelyWeb版Smalltalk-72について」にもお示しした勝手バックアップをご利用ください→https://lively-web.org/users/sumim/ALTO-Smalltalk-72.tweak2.html

追記 [2021-11-06]:削除されて使えなくなってしまっていた本家ですが復活しました。ただ、少々古いバージョンへのロールバックなのでマウスクリックが拾えないなどの諸々も戻っています。引き続き勝手バックアップをご利用ください→https://lively-web.org/users/sumim/ALTO-Smalltalk-72.tweak2.html

組み込みの構造化エディター(Enter、Leave、Exit)に続く)