Smalltalkは、アラン・ケイの「“オブジェクトへのメッセージ送信”というメタファーを用いることで、構築するソフトウエアにいろんな意味での徹底した遅延結合性をもたらす」という発想に基づいて実装された言語(厳密には、言語処理系を内包したコンパクトな仮想OS。自身も自身で記述され、シンプルさと遅延結合性を重視した実装を目指している←これ、他にない重要なところ!)で、70年代に、Smalltalk-72、Smalltalk-76、Smalltalk-80という大まかには三世代の大変革を遂げて今のかたちに落ち着いています。それぞれ言語としての特徴があり、メッセージングというコンセプトをキーにしていることを除けば、まったく別の言語と言っても過言でないほどの違いがあります。
他に、Smalltalk-71というのもよく出てきますが、これは言語仕様の一部が考えられただけで、実装は存在しませんでした。なので、カール・ヒューイットのアクター理論がSmalltalkのメッセージングの影響を受けている…と言うとき(逆では?と思う人はこちらの謝辞を読みましょう→Hewitt73)にヒューイット自身の発言を含めてよく引き合いに出される「Smalltalk-71」は、最初に動作したバージョンであるSmalltalk-72のおそらくは勘違いで、そう読み替えて差し支えないと思います。なお、Smalltalk-71はヒューイットらのPLANNERに大きな影響を受けているのだそうで、これがまた混乱に拍車をかけていておもしろいです(おもしろがっていてはいけないのですが…^^;)。
閑話休題。
Smalltalk-74と78は、それぞれ72と76の類縁です。74の詳細はまだ調べ切れていませんが、72のGUI等の改良版をこう呼んだのではないかと思います。78は今回扱うバージョンで、「暫定ダイナブック」という文脈においてのALTOの後継であるNoteTakerというポータブルコンピュータ(非公式ではありますが、空港の待合ロビーでバッテリー駆動で使用された史上最初のGUIを備える可搬式PCで、有名なオズボーン1などのタイプの可搬式PCの元ネタでもあります)向けに、76を機能拡張してなおかつコンパクトにしたバージョンです。
Smalltalk-72と76についてはすでに、それぞれSqueakとJavaで実装されたエミュレーターがあったのですが、後者についてはGUI部分がJavaのSWINGを使って表現されていたため(ブートアップに当時のコードが使われていたり、処理系としてはかなりの本格再現にもかかわらず)なんとなくコレジャナイ感が否めませんでした。
が、このたびSmalltalk-72~76、およびSqueak転生の中の人であるダン・インガルスらによって、もっか彼の進めているプロジェクトLively Kernel上に実現された(NoteTakerのエミュレーターの上で動く)Smalltalk-78エミュレーターは実に再現度が高く、当時のままのGUIでその使い勝手を楽しめます。旧Macが作った、良くも悪くも現在主流のWIMPなGUIの流れのルーツ(つまり、スティーブ・ジョブズはゼロックスで何を観て開眼したか?)に興味がある私のようなマニアには文字通り必見の環境を、映像で見るだけでなく、自分で操作することが可能になったわけです。本当に夢のような話。
さて、例によって前置きがずいぶん長くなってしまいましたが、前述の以前作られたSmalltalk-76のエミュレーターを使って遊んでみたのが古いこちらの はてなダイアリーのエントリーになります。- Ruby は Smalltalk-76 に似ている。by アラン・ケイ
- Ruby は Smalltalk-76 に似ている。by アラン・ケイ 2
- Ruby は Smalltalk-76 に似ている。by アラン・ケイ 番外編
これをせっかくだからSmalltalk-78のエミュでも試してみようという企画…のつもりだったのですが、ここまでの前置きであろうことか力尽きてしまったので(←おいっ!^^;)制御構造についてだけ抜粋してお茶を濁すことにます。(昔の記事を今読み返してみてつくづく思うに、こんなのをさらっと書けた昔はなんとパワーに満ちあふれていたのでしょう… orz)
なお、似たUnicode文字を代用しているため一部環境では正しく表示されないかもしれませんが Smalltalk-76 の if⦂ などの ⦂ の部分は、オープンコロンと呼ばれる白抜きのコロン(:)が使われます。
▼条件分岐 `"Smalltalk-80 以降では、通常のメッセージ式で" 3 < 4 ifTrue: [5] ifFalse: [6]`"Smalltalk-76" if⦂ 3 < 4 then⦂ 5 else⦂ 6
#ruby if 3 < 4 then 5 else 6 end
"Smalltalk-76" for⦂ str from: ('a', 'b', 'c') do⦂ [user show: str; cr] " 'a'、'b'、'c' を表示 "
#ruby for str in ["a", "b", "c"] do p str end # "a", "b", "c" を表示 ["a", "b", "c"].each {|str| p str} # ブロック付きメソッドでも
"Smalltalk-76" n ← 0. while⦂ (n ← n + 1) < 10 do⦂ [n print. user cr] until⦂ (n ← n - 1) = 0 do⦂ [n print. user cr]
#ruby n = 0 while (n += 1) < 10 do p n end until (n -= 1) == 0 do p n end
恥ずかしながら以前はSmalltalk-76とRubyとの字面上での類似性にしか興味がなくて、この制御構造がどのようにメッセージングパラダイムで実現されているのか、よく調べませんでした。
で、今回あらためて調べてみたのですが、なかなかおもしろい仕組みで動いていました。簡単に言うと、Rubyでselfを省略できるように、Smalltalk-76のパーザーは、レシーバーのないメッセージ式を許す仕様になっているのです。
つまり、while⦂ (n ← n + 1) < 10 do⦂ [n print. user cr]
という式は、
something while⦂ (n ← n + 1) < 10 do⦂ [n print. user cr]
というように、ある省略されたレシーバー(ここでは仮にsomething)に対するメッセージとして解釈されるわけです。これはまたSmalltalk-80にはない Rubyに似ているところ&面白い仕組み を思わず発見してしまいました。w(あ、ここで「Rubyに似ている」というのはレシーバーを省略する記述を許すところだけで、Rubyがwhileなどの制御式を同様に解釈したり内部的にメッセージングで処理してるとかではありません。為念)
実際にはもう少し複雑で、while⦂do⦂ というセレクターはマクロ(というか特殊形式)として認識されて、さらに下請けの Generator>>#whiledo:args: というコンパイラオブジェクトのメソッドが実行時には起動されます(つまり先の省略された something はコンパイラオブジェクトだったというわけです)。
ちなみに制御構造としてあらかじめ定義されていないメッセージのみ(キーワードメッセージに限る)の式を評価しようとすると、Context に定義された同名のメソッドを探してあればそれをコールするよう処理され、それもない場合は UNKNOWN CONTROL MESSAGE としてコンパイラにしかられます(文法エラーではないところがミソですね)。
もうひとつ余談ですが Smalltalk-72 でも条件分岐やループを'Smalltalk-72' if 3 < 4 then 5 else 6 for n to 10 (n print. disp ← 32)
などのように書くことができるのですが、これはifとかforというオブジェクトがあり、それに対して 3 < 4 then 5 else 6 というメッセージを送る(ifの場合)という変態仕様でした(Smalltalk-72はリーダーマクロのようにまるで文法を定義するかのごとくメソッドを記述させる仕様のため、それ以降のSmalltalkと違い遅延評価のための特別なしくみを必要としません)。これはこれでまた奥が深いですね。