ClosureCompiler

これまで、クロージャで遊びたいときには Squeak では駄目*1なので、Strongtalk *2VisualWorks*3 を使っていた*4のですが、この ClosureCompiler を使えば、その必要はなくなりそうです。インストールにちょっとコツがいるのでメモしておきます。ポイントは、いったん古いバージョンをインストールしてから、SqueakMap 経由で上書きすること。 試したのは、現在入手可能な最新の開発途上バージョンの Squeak3.8g-6548 (.zip, 8.7Mb。他に Squeak VM、SqueakV3.sources、各種プラグインなどは別途入手の要あり) 。Squeak3.7 でも、すくなくともブロッククロージャを使うことはできるみたい*5です。

  1. ClosureCompiler2-ajh.zip を入手して Squeak3.8 のフォルダ内に展開
  2. Squeak を起動
  3. open... -> file list でファイルリストを起動
  4. ./ClosureCompiler2-ajh/README.txt を開き、そこに書かれた手順1〜3を行なう
    1. open... -> SqueakMap Package Loader で SqueakMap を起動
    2. Refactoring Browser for 3.8 をインストール
    3. SmaCC Smalltalk Compiler-Compiler-Development をインストール
      (適正バージョンがないので最新版を強制インストール)
    4. README.txt に戻って、スクリプトを選択して do it (alt-/cmd-d)
  5. SqueakMap に戻って、Compiler をインストール
    (ダイアログポップアップには Yes )
  6. 引き続き、ClosureCompiler をインストール


ブロックをクロージャ(a BlockClosure)として使いたいときは、help.../appearance... -> preferences... -> general -> #compileBlocksAsClosures を on 。普段(eToys するときなど)は off 。


ブロックがクロージャになっているときは、次のようにブロックを再帰させるようなスクリプトも評価して値を得る( print it (alt-/cmd-p) )ことができます。

fact
fact _ nil. " コンパイル時警告の回避用ダミー " fact _ [: nn | nn < 2 ifTrue: [1] ifFalse: [nn * (fact value: nn - 1)]]. ^ fact value: 10
=> 3628800


前に触れた、プライオリティの異なるマルチスレッドを記述する際の、ブロック変数がらみの問題も起こりません。

stream forGoingOn defaultPriority putSemaphore
stream _ String new writeStream. forGoingOn _ Semaphore new. putSemaphore _ Semaphore new. defaultPriority _ Processor userSchedulingPriority. [($1 to: $3) do: [: each | stream nextPut: each. putSemaphore signal]. forGoingOn signal] forkAt: defaultPriority. [($a to: $c) do: [: each | putSemaphore wait. stream nextPut: each]. forGoingOn signal] forkAt: defaultPriority + 1. forGoingOn wait; wait. ^ stream contents

#compileBlocksAsClosures が on(ブロックが a BlockClosure)のとき。

=> '1a2b3c'

#compileBlocksAsClosures が off(ブロックが a BlockContext)のとき。

=> '112233'


あと、この拡張によりなぜかブロック変数が擬変数でなくなる(代入できるようになる)ので、技術野郎の復讐のアキュムレータの例では、おそらく最も短く、比較的簡潔に書くことができる言語のひとつとなります。w

FOO_[:n|[:i|n_n+i]]

ちなみに Common Lisp では(同じく詰めて書いた場合でも)これが精一杯(^_^;)。

(defun foo(n)#'(lambda(i)(incf n i)))

使用例:

foo
foo _ FOO value: 100. World findATranscript: nil. 10 timesRepeat: [Transcript space; show: (foo value: 1)]
=> 101 102 103 104 105 106 107 108 109 110

まあ、定義は短くても、いざ使うときには冗長になるので、やっぱり負けは負けなんですが……

続く


*1:Squeak は、XEROX Smalltalk-80 の流れこそ汲んでいるのですが、非常に古いバージョンを基に作られたため、90 年代に生まれた比較的新しい Smalltalk であるにもかかわらず、80 年代当時の古典的仕様や、その後の進化の過程で他の Smalltalk 処理系が手放してしまった特徴を、いまだにいくつか残しています。ブロックがクロージャになっていないことも、そうした“先祖返り”的側面の一つです。

*2:オプショナルながら静的型チェックが使える、クラスでなくミックスインベースの“変わり種”SmalltalkJava も関係する VM 高速化技術がらみにおいても、知る人ぞ知る無視できない存在。

*3:由緒正しい XEROX/ParcPlace Systems Smalltalk-80 の流れを汲む、純正 かつ 近代的 かつ 最新 の Smalltalk 処理系。現在は Cincom が開発/販売。 駄目ですよ。 Smalltalk-80 が滅びたとか、開発が止っているとかいうデマを信じちゃ…。w

*4:他に、私の手元の環境では動かせないのですが、ANSI 準拠の GNU Smalltalk など、とにかく Squeak のように異常に古い処理系でなければ、たいていブロックはクロージャになっているはずです。あと、余談ですが、smalltalk.org や GNU Smalltalk が本家というのはデマです。念のため。

*5:なにしろコンパイラの仕様をもいじる大がかりな拡張なので、他の部分との整合性がどうなっているのか、私のような素人にはさっぱりなものでして…。