Smalltalkのつまみ食い

ゆの in language は面白かったけれど、ハッカーとかギークだとか、その気のある人しか楽しめないと凪瀬さんは言う

「秘密結社的な壁」と言われるとなんだかカッコよすぎで、「ちょっと大げさ・そこまで高い壁じゃないよ」と言いたくなっちゃうけれど、ようするに変態は変態を識るということですね、と言い換えると、なるほど、壁は高そうですw(いぬかみっ!のようです...)

実際、我が身を振り返れば、恥ずかしながら、よぅわからんネタが結構あったなぁと(^^; このお祭りをフルで楽しめたのは そうとうの変態、もとい、よほどの言語マニアとかハッカーさんに限られたのかもしれません。



しかしそれではちょっともったいないです。特に、ゆの in Smalltalk は、ゆの in Java の 純粋にハッカー達のすばらしき変態さ(ほめ言葉)を楽しむパターンとちがい、Smalltalk の変態さ(ほめ言葉)も併せて愉しんで欲しいところ。

そこで、ゆの in language をきっかけに Smalltalk にちょっぴり興味をもった、 ハッカーでもギークでも無い フツーのプログラマのために、お手軽に Smalltalk の 変態さを楽しむための、つまみ食いツアーを作ってみました。*1

まずは心の準備

Smalltalk を試すにあたって、まずはちょっとした心の準備。

Smalltalk は実は OS(のようなもの) とそのスクリプティング言語、その両方の名前です。Smalltalk OS(のようなもの) は Smalltalk 言語で実装されていて、Smalltalk 言語で拡張することが出来ます。

Smalltalk OS(のようなもの)は、デスクトップというメタファを持った GUI 環境の最初のものです。今では普通の環境ですが、Smalltalk が世の中に出た当初はめずらしく、その使い方が本一冊になったくらい。



(オレンジ色の本がまるまる環境の説明になってます)

(バリバリの技術書なのにマウスとは?ダブルクリックとは?という内容に萌えます)


なお、Smalltalk OS(のようなもの) は、OS(のようなもの) といっても、バーチャルマシンの上で動作するので、VirtualPC とか VMWare 上で別のOSを動かすのと同じ様な感じでお手軽にインストールすることができます。


Smalltalk をインストールする

SqueakオープンソースSmalltalk の実装です。今回はこちらを使います*2。必要なのは

  • バーチャルマシン
  • バーチャルマシン上で動かすイメージ などなど

です。

まず、バーチャルマシンをダウンロード。スクイークランドのDeveloper Download から、VM をダウンロードします。*3ダウンロードしたファイルを解凍して出てきたフォルダを、好きなところにおいてください。


次にバーチャルマシン上で動作させるイメージファイル等々をダウンロードします。umejavaさんちの Squeak開発者版 のページのSqueak 3.9 というところから、イメージファイルとソースファイル、

  • Squeak3.9-final-7075J.zip
  • SqueakV39.sources.zip

をダウンロードして解凍します。で、出てきたファイルをさっき展開した VM のフォルダに移動します。

最後に、Squeak3.9.1-final-7075J.image というファイルを Squeak.exe にドロップすれば Squeak Smalltalk 環境が 起動して、こんな画面が開くハズ。



これで準備OK!

とりあえずプログラムを書いてみよう

いよいよ Smalltalk プログラミングの開始です。デスクトップをクリックすると(右クリックではないですよ!)[ワールド] メニューが開きます。で、

[ワールド]メニュー → [開く] → [ワークスペース]

とたどると、テキストエディタ(のようなもの)= Workspace が開きます。とりあえずそこに、一気にこのコードを入力しちゃってくださいまし。

| pen |
pen := Pen new roundNib: 8;
                color: Color red.
                
[Sensor yellowButtonPressed not] whileTrue: [
    Sensor redButtonPressed
        ifTrue: [pen goto: Sensor cursorPoint]
        ifFalse: [pen place: Sensor cursorPoint]]

(このコードのネタ元:Smalltalk でビビること


入力し終えたら、

の様に、コードを全部選択して、右クリックでメニューを出して、[式を評価] を選択します。

すると、クリックしながらマウスを動かすと 線が引ける、簡単なお絵かきプログラムが動作します。(このプログラムは右クリックで終了します。)

このコードを肴にSmalltalk をつまみ食いしようというのが今回のネタであります。

メッセージ・メソッド・オブジェクト

まずは Smalltalkパラダイムをササッとなでなで。一言(?)で言えば、


(ネタ元:http://www.sra.co.jp/people/aoki/OOAOOD/01.pdf)


な感じです。(図のマルがオブジェクト)

外部から依頼のことをメッセージ(message)と言います。 外部からの依頼に基づきオブジェクトは自身で最適な 依頼達成の手法を探し、それを実行します。 この依頼達成の為の手続きをメソッド(method:手法の意)と言います。 オブジェクトは状態を持ちます。 この状態の保持領域のことをフィールド(field)と言います。

以上を念頭において、いよいよ Smalltalk プログラムを読み解いていきましょう。

ペンを生成するところ を読んでみる

| pen |
pen := Pen new roundNib: 8;
                color: Color red.

コード中の | pen |は、一時変数(ローカル変数)宣言、:= は代入になります。 この行は、 := の 右側の式の評価結果を 一時変数 pen に代入するコードです。Smalltalk は文末は . で区切るので、インデントは単なる見栄え上のものになります。

まずは右側の式の、セミコロン; の前まで読んでみます。最初のメッセージ、new のレシーバ Pen は クラス です。Pen に new メッセージを送ることにより、Pen は インスタンスオブジェクトを生成して return します。そして return された Pen のインスタンスオブジェクトに キーワードメッセージ roundNib: 8 が送られます。

ブラウザを使ってみる

さて、このメッセージで実行される Pen>>#roundNib: とはどんなメソッドなのでしょう。 ちょっと調べてみましょう。 オブジェクトの事はオブジェクトに聞くのが一番です。

Workspace の別のところに

Pen browse

と書いて [式を評価] してみましょう。すると 「システムブラウザ」という、Smalltalk の中のクラスやメソッドを ぶらぶら眺める為の ツールが起動します。



上段は 左から順に、「クラスカテゴリ」「クラス」「メソッドカテゴリ」「メソッド」と並んでいます。 右上のペインをスクロールすると roundNib: メソッドが見つかるはずです。 選択すると以下のようなメッセージが表示されます。

roundNib: diameter
    "Makes this pen draw with a round dot of the given diameter."

    self sourceForm: (Form dotOfSize: diameter).
    combinationRule := Form paint.

ここのコメント(「"」「"」で囲まれた部分)を読むと、 なんか引数で与えた直系のまぁるいペン先を作ってくれる様ですね。

カスケードとメッセージの優先順位

続いてセミコロン; の後ろ側。 ; は、前のメッセージと同じレシーバにメッセージを送る際にレシーバの記述を省略する便利な記法です。 こういうのをカスケードと言います。この場合、メッセージ color: ... は前のメッセージ roundNib: 8 と同じく Penのインスタンスオブジェクトへ送られます。

color: Color red. の実行順は Color red が先に実行され(red は、Color クラスに 赤 な状態の Color の インスタンスを作れ、というメッセージ)、 結果リターンされたオブジェクトが color: の引数となります。 Smalltalk のメッセージの実行順は基本的に 左から右 ですが、メッセージの種類により優先順位があり、 「引数のないもの」「数式のように見えるもの」 「hoge: foo の様な引数を取るもの」の順に実行されます。 また、 ()で括ることで優先順位を強制的に買えることも可能です。

で、color: メッセージの戻りはレシーバ自身(Pen のインスタンス)であるため、最終的に 直径8の丸いペン先で、赤色に初期化された Pen のインスタンスオブジェクトが変数 pen に代入されるわけです。

色気を出して カスケードまで登場させてしまいましたが、実はこのコード、手続き風に書いた

| pen |
pen := Pen new.
pen roundNib: 8.
pen color: Color red.

と と同じです。(こっちの方が読みやすいかしら)

というわけで、ここまでのコードを Python で書くとこんな感じです。

pen = Pen()
pen.rondNib(8)
pen.color( Color.red() )

線を描くところを読んでみる

[Sensor yellowButtonPressed not] whileTrue: [...]

の部分に進みましょう。

まずはコード本体に入る前に豆知識。Smalltalk は あまりに古い GUI OS(のようなもの) なので、マウスのボタンの呼び方がちょっと特殊です。「右ボタン」とか「中ボタン」、「左ボタン」じゃなくて、「赤ボタン」、「青ボタン」、「黄ボタン」と呼びます。(豆知識 ここまで)

さて、コードに戻ります。

ブロックもオブジェクト

[ ]で囲まれた部分をブロック と言います。C言語のブロック {...} とだいたい同じですが、Smalltalk がひと味違うのは、ブロック ですら オブジェクトというところ。プログラム自体をオブジェクトにしたものです。オブジェクトなので、メッセージの引数として渡したり、はたまた ブロックそのものにメッセージを送ったりすることが出来ちゃうのです。

たとえばこのコードは、[Sensor yellowButtonPressed not] オブジェクトをレシーバに、 whileTrue: [...] メッセージを送るコードです。whileTrue: は、ブロックに 「自身を実行して、もし結果が 真だったら、引数で渡したブロックを実行し、再帰する」という メッセージです。要するにwhile ループなのですが、Smalltalkは これを 制御構文という(言語の中で)特別なものでなく、ごく普通のメッセージで実現しています*4

グローバルなオブジェクト

さて、それぞれのブロックコンテキストの中の処理について見てみましょう。

最初の[Sensor yellowButtonPressed not] ですが、Sensorオブジェクトに yellowButtonPressed メッセージを送って返してきたオブジェクトに not というメッセージを送ってる――ということまでは良いと思います。 しかし、この Sensor オブジェクトは誰も生成していませんし、 クラスでもなさそうです。

なんだろ?と思ったら本人(オブジェクト)に聞くのが鉄則

Sensor class browse

を [式を評価] してみれば、EventSensorクラスが開きます。 つまり Sensor とは EventSensor クラスのインスタンス オブジェクトです。

Smalltalk では大文字で始まる変数はグローバル変数で、システムのどこからでもみることが出来ます。また、ブラウザで閲覧出来るクラスも実はクラスオブジェクトと呼ばれるオブジェクトの一種で、それが同名のグローバル変数に束縛されているだけだったり。

キーワードメッセージ

ここまで説明して来ませんでしたが、

ifTrue: [pen goto: Sensor cursorPoint]
ifFalse: [pen plase: Sensor cursorPoint]

のような : で終わる名前で 引数をとるようなメッセージをキーワードメッセージと言います。 実は ifTrue:部 と ifFalse:部は併せて一つのメッセージで、

ifTrue: hoge ifFalse: piyo

は、C言語風に書くと、

ifTrueIfFalse( hoge, piyo )

に相当します*5。かなり変わってるでしょう?

ポリモーフィズムがあればいい

さて、この ifTrue:ifFalse: メッセージを受けている オブジェクトは、一体どのクラスに属するのでしょうか? えっ? フツーに考えて Boolean だろっ、、ですって?、、、にまま。それはどうかしら。

疑問に思ったら見てみましょう。

Sensor redButtonPressed class browse

を、[式を評価]してみましょう。すると...。

False クラス?? 値じゃ無くって型なのですか...。なんだかヘンなの来ちゃいました。

実は Smalltalk、if-else 構文のような物までも、ポリモーフィズムで実現しちゃうのです*6。試しに ifTrue:ifFalse: メソッドを True クラスと False クラスで見比べてみると、

True >> ifTrue: trueAlternativeBlock ifFalse: falseAlternativeBlock 
    ^trueAlternativeBlock value
False >> ifTrue: trueAlternativeBlock ifFalse: falseAlternativeBlock 
    ^falseAlternativeBlock value

と、なっちゃったりしています。...やりすぎっていうなーっ!


* * *


SmalltalkOOP の世界観ではプログラムを、オブジェクトがメッセージを介して協調する、分散協調系としてシステムを記述します。Smalltalk の文法は、このような世界観を ダイレクトに表現していて、プログラムの殆どすべてがメッセージ式で記述されます。

特に、前のメッセージのリターンオブジェクトに 次々とメッセージを投げていくメッセージチェーン を Smalltalk の文法は実に気持ちよく書くことができます。

今回のコードはクラスは一つも定義していないのですが、でもでも OOP してるでしょ? OOP は決してクラスをプログラミングすることじゃありませんことよ、とちょっとカッコつけて言ってみるテスト。


* * *


Smalltalk をもっと知りたいと思ったなら、私のお薦めは sumim さんのモサ伝:SqueakではじめるSmalltalk入門・アーカイブ です。(このエントリは第4回劣化コピーです)


また、タイムリーなことに 青木さんの Smalltalk 本が近日発売予定です。

SMALLTALKで学ぶ オブジェクト指向プログラミングの本質

SMALLTALKで学ぶ オブジェクト指向プログラミングの本質

ゆの補足

日本語環境で、バックスラッシュ\ が円マーク¥ に化けるように、Smalltalk 環境では、_ は ← に、^ は ↑ に化けます。そして もともと Smalltalk の代入 は ← で行っていました。後に := も代入に使えるようになり、今ではすっかりこちらが幅をきかせるようになってしまいました。字形も _ にされちゃいましたし、しくしく。(Squeak では ほんのつい最近まで ← だったのです)

でも、^ はまだ ↑で表示されるのですよね。

追記

このエントリは、「『わかる奴だけ楽しめばいい』という秘密結社的な壁」は、壁の内側の人も外側の人も望んで設けたものじゃない、内側の人はどんどん乗り越えて入ってきて欲しいし見て欲しい(変態は見られたがりなのです)、外側の人は塀の中の懲りない面々の様子が愉しそうでとても気になる。利害が一致してるなら、えいっ、はしごかけちゃえ!という趣旨のもの。

できれば登って(変態仲間に)入ってきて欲しいし、登り切る元気がないなら塀の上に頭だけ出して覗いてくれるだけでも僥倖です。そういう意味でこのエントリより もっといい はしごががあります。

わずか 30分で Smalltalk のコードが読めるようになっちゃう優れもの!

あぁ、可憐です。umejavaさん。

*1:ウチみたいなマイナーなblog に Smalltalk を知らない人がいっぱい来る機会は、そうそう無いので(w こりはチャーンス! と Smalltalk 紹介記事をアップした次第。なのでゆの in ...がどうのは実は単なる口実です(^^;。

*2:Smalltalk の実装は 本当に、本当にたくさんあるのですが、「本家Smalltalk」 な Squeak と「元祖Smalltalk」な VisualWorks がとりあえず 2大メジャーといったところです

*3:ここで公開している VM は、ちょっと古いのですが、日本語パッチが当たっているので、こちらをオススメします。

*4:建前上は。→whileTrue: の罠 - みねこあ

*5:ホントは、: も含めて名前なので、ifTrue:ifFalse:( hoge, piyo ) といったところなのですが、C では : は識別子に含められないので、「どこがC言語風やねん!」という突っ込みを怖れて、ちょっとインチキな例を書いちゃいました(^^;

*6:建前上は。→whileTrue: の罠 - みねこあ の sumim さんのコメントを参照