最低限の Squeak Smalltalk 入門
Smalltalk、特に Squeak Smalltalk は、言語処理系や IDE というより、あたかも“ゲストOS”のように振る舞うので、ちょっと試してみたいと思い立ってインストール&起動まではできたものの、そこから先には、独自の GUI や操作スタイルに阻まれて進めなかった…という経験をお持ちの方も多いと思います。
そこで、Smalltalk 環境独自の GUI ツール群の使い方はあとで覚える(あるいはそもそもそれほど興味がない←しょぼーん…(^_^;))として、今のところは、試してみたいコードを動かしたり、どんな動作になるのか確認ができればいい…といった忙しい人向けにざっくり、Smalltalk 式を評価するための方法のようなものを書いてみました。
とりあえず、マシンが Mac 以外(といっても、Mac の場合の違いは、ctrl + d とかが cmd + d になるだけ…)で、Squeak Smalltalk 環境は umejava さんの日本語版 Squeak3.9 のインストールおよび起動済みという前提で、オーソドックスな「カウンタ」オブジェクトを作ります。
なお、以下には、しょぼいながらも Smalltalk で「カウンタ」を記述するコードが出てくるので、まずは自分で考えてみたい場合はそれを終えてから読み進めたほうがよいと思います(当初「ネタバレ」と書いたワケはこれです)。
■ ブロック(無名関数)版カウンタ
ツールはいっさい使わないようなことを書きましたが、コードを入力する場所くらいは用意しないといけないので、とりあえず、デスクトップクリック → 開く… → ワークスペース、で「ワークスペース」を起動します。ワークスペースというのは、Smalltalk-80 時代からある書き捨て用のメモソフトです。加えて最近のワークスペースには、宣言無しに出現した変数を半永続的な「ワークスペース変数」というある種のグローバル変数と見なしてくれる便利な(かつ、場合によってはやっかいな ←たとえば変数名のスペルミスを警告&修正する機能が用をなさなくなる!)機能も付いています。
ワークスペースを立ち上げたら、そこに、次のコードをタイプして入力するか、ここからコピペして貼り付け(ctrl + v)ます。
counterMaker := [:n | [n := n + 1] fixTemps]. c1 := counterMaker value: 0. c2 := counterMaker value: 100
ctrl + a ですべてを選択し、do it (ctrl + d。右クリックの「式を評価」と同じ) します。ctrl の代わりに alt も使えます。これでワークスペース変数である c1、c2 にカウンタが代入されました。あとは、
c1 value
もしくは、
c2 value
をタイプして入力し、print it (ctrl + p。右クリックの「式を表示」も同じ) で各カウンタのインクリメントおよび内容の確認ができます。両方同時に確認したいときは、
{c1 value. c2 value}
を print it (ctrl + p) するとよいでしょう。
■ クラス・インスタンス版カウンタ
通常、Smalltalk 環境ではクラスやメソッドの定義にはクラスブラウザという独自の GUI アプリを用いるのですが、ここでは引き続きワークスペースを使って、定義などは Smalltalk 式を評価することで行なうことにします。以下はあくまでクラスブラウザなど独自ツールの使い方は知らない&今はワケあって学んでいられない人向けですので、どうぞその点、あしからず誤解の無きようお願いいたします。
次の式を入力して選択し、do it (ctrl + d) します。カウンタクラス「Counter」の作成です。
Object subclass: #Counter instanceVariableNames: 'counterValue' classVariableNames: '' poolDictionaries: '' category: 'Category-Name'
ながったらしいですが、これも式です。レシーバで、これから作る Counter の将来のスーパークラスでもある Object に対して「subclass: #Counter instance... 以下略」というメッセージを送ることで、#subclass:instanceVariableNames:classVariableNames:poolDictionaries:category: という長〜い名前のメソッドをコールしています。同じことを Pythonや Ruby など一般の OOPL 風に書きなおすと、
Object.subclass_instanceVariableNames_classVariableNames_poolDictionaries_category_("Counter", "counterValue", "", "", "Category-Name")
といった感じでしょうか。見た目はともかく、たとえば Python の、
Objects.subclass("Counter", instanceVariableNames="counterValue", classVariableNames="", poolDictionaries="", category="Category-Name")
といった凝った仕組みを持つメソッド呼び出しとは異なり、ごく普通の単純なメソッド呼び出しに過ぎない(コールされるメソッド名がやたら長いことをのぞけば…)というところが“ミソ”(つまり、Smalltalk の式は、じつのところ、その見た目からうける印象ほど複雑な意味を持っていない…ので恐るるに足らず!)かも。
閑話休題。
続けて、初期化メソッドを定義します。クラスブラウザはあえて使わず、メソッドを定義する次の式を do it (ctrl + d) 。
Counter compile: 'initialize counterValue := 0'
do it の直後、メソッド定義のタイミングでイニシャルを入れろと入力欄がポップアップすることがあります。適当に入力し「了解」ボタンを押して消してください。キャンセルしたり何も入力しないとしつこく現われるので必ず何か入れてやってください。この情報は、Utilities setAuthorInitials の do it (ctrl + d) であとから変更できますし、そもそも終了時にイメージを保存しなければ保持されないのでツール群(主にメソッドのバージョン管理機能)を使わない今は入力内容は適当で大丈夫です。
続けて、同様にカウントアップメソッドを定義します。
Counter compile: 'pushButton ^counterValue := counterValue + 1'
これでインスタンスを作って確認できます。式の評価は do it (ctrl + d) です。
c3 := Counter new
インクリメントおよび内容の確認は #pushButton をコールします。式の評価と結果の挿入は print it (ctrl + p) です。
c3 pushButton "=> 1"
おまけ。インスタンス変数「counterValue」のアクセッサメソッドを自動生成しましょう。
(Browser newOnClass: Counter) createInstVarAccessors
上の式を do it (ctrl + d) すると getter と setter がそれぞれ #counterValue と #counterValue: という名前で定義されます。
c3 counterValue "=> 1 "
c3 counterValue: 100; pushButton; pushButton "=> 102 "
副作用でクラスブラウザも開くので、続きはこれの使い方を覚えがてら遊ぶのもよいかもしれません。
もうおなかいっぱいでしたら、ここでおしまい。逆にモーレツに興味がわいてきたら、英語ですがこちらのチュートリアルに進んでみてはいかがでしょう。
日本語版のメニュー項目を英語に切り替える(チュートリアルに合わせる)には、次の二つの式を選択して do it (ctrl + d) 。
Locale switchToID: (LocaleID isoLanguage: 'en'). Locale currentPlatform: (Locale isoLanguage: 'ja')
付録: 似たようなことを GNU Smalltalk 3.0 で
Squeak Smalltalk 環境は、1980 年代の古き良き Smalltalk-80、さらにさかのぼって 1970 年代の ALTO や Notetaker などのマシンを“暫定ダイナブック”として機能させるための GUI OS としての名残りを色濃く残しているので、独自の GUI や操作スタイルが鼻につくこともあるかもしれません。そんな向きには、CUI(ターミナルソフト)でスクリプト言語っぽく使えて、“驚き最少”で入門できる GNU Smalltalk がお薦めです。
ただ、Smalltalk の高い(…と、される)生産性の半分は GUI や独自のツール群との連携により支えられている…ということもあるので、そうした Smalltalk の“秘密”を垣間見てみたいという好奇心をお持ちでしたら、CUI 中心の GNU Smalltalk で満足して終わりにはせずに、いずれは GUI 付きの Smalltalk 処理系に、一度どっぷり浸かってみる機会を持つことも考えたほうがよいと思います。老婆心ながら、念のため。
$ gst -v GNU Smalltalk version 3.0.1 <snip> $ gst GNU Smalltalk ready st> counterMaker := [:n | |m| m := n. [m := m + 1]] a BlockClosure st> c1 := counterMaker value: 0 a BlockClosure st> c2 := counterMaker value: 100 a BlockClosure st> {c1 value. c2 value} (1 101 ) st> {c1 value. c2 value} (2 102 ) st> {c1 value. c2 value} (3 103 ) st> Object subclass: Counter [ st> | counterValue | st> initialize [counterValue := 0] st> pushButton [^counterValue := counterValue + 1] st> ] st> c3 := Counter new initialize; yourself a Counter st> c3 pushButton 1 st> c3 pushButton 2 st> c3 pushButton 3
Squeak Smalltalk のブロックと違い、GNU Smalltalk のブロックはクロージャなので #fixTemps のコール(ブロックの実行時環境の複製)は不要です。ただブロック変数は代入不可なので、別にテンポラリ変数(ここでは m )を宣言し、これを用いる必要があります。
クラス定義の構文「《スーパークラス名》 subclass: 《新規クラス名》 [ ... ] 」や、その中で使われているメソッド定義文「《メッセージパターン》 [ ... ] 」は、3.0 から採用された GNU Smalltalk 独自のもので、これらはメッセージ式(≒ Smalltalk の式)ではありません。ご注意あれ、かし。
付録その2: 似たようなことを Dolphin Smalltalk で
くどいようですが、あくまで Smalltalk 独自のツール群の勉強をしている暇がないときに、最低限の操作とワークスペースのみでとりあえず…という特殊な想定ですのであしからず。勉強会などでは、事前にクラスブラウザの使い方を覚えてからそれを使ってください(^_^;)。
ブロックについては GNU Smalltalk と同じ注意(#fixTemps が不要、ブロック引数は代入不可、#initialize が自動的に呼ばれない)に加えて、 Squeak Smalltalk(と、その影響を受けた処理系)独自の { ... } という配列リテラルが使えないことを除けば Squeak Smalltalk と同じ式が使えるみたいです。
ただし式の評価は Squeak Smalltalk での do it (ctrl + d) や print it (ctrl + p) ではなく Evaluate It (ctrl + e) および Display It (ctrl + d) な点に注意。ワークスペースは、起動時に現れるウインドウの Workspace アイコンのダブルクリックにより呼び出せます。
counterMaker := [:n | | m | m := n. [m := m + 1]]. c1 := counterMaker value: 0. c2 := counterMaker value: 100.
Array with: c1 value with: c2 value. "=> #(1 101) "
Array with: c1 value with: c2 value. "=> #(2 102) "
Array with: c1 value with: c2 value. "=> #(3 103) "
Object subclass: #Counter instanceVariableNames: 'counterValue' classVariableNames: '' poolDictionaries: '' category: 'Category-Name'.
Counter compile: 'initialize counterValue := 0'.
Counter compile: 'pushButton ^counterValue := counterValue + 1'.
Counter class compile: 'new ^super new initialize; yourself'.
c3 := Counter new. c3 pushButton. "=> 1" c3 pushButton; pushButton; pushButton. "=> 4 "
Dolphin Smalltalk はほとんど使ったことがなかったのですが、Squeak Smalltalk のしょぼさにくらべると、補完機能の出来がすばらしいですね(ちなみに Squeak Smalltalk では alt + q の連打で補完される)。総じて、Smalltalk-80 直系の子孫である Squeak Smalltalk や Cincom Smalltalk より、過去のしがらみがないのがよいのか、今風の IDE っぽさが板に付いている感じがします。舞波さんお薦めなのもうなずけます。