Clojure(参考記事)はJVM向けのLISP風言語である。Clojureで重視している点の1つに並行処理があり、Clojureでは不変のデータ構造で並行処理をサポートしている(Clojureは永続データ構造を装備(リンク))。もう1つの特徴がソフトウェアトランザクショナルメモリ(STM=Software Transactional Memory)であり、STMではロック(Lock)やミューテックス(Mutex)の代わりにトランザクションを使って共有メモリの更新が可能である。STMは未だに真価が問われている賛否両論の技術ではあるが、JVM上の実装にアクセスすることで、簡単にそれ(STM)を試してみることができる。
Clojureは間もなく1.0がリリース予定で、すでに多数のClojureライブラリが利用可能になっている(リンク)。Rubyのライブラリからインスピレーションを得たものもあり、以下にいくつか挙げてみた。
- Compojure(リンク)はClojureのWebフレームワークで、READMEによると、RubyのWebフレームワーク「Sinatra」にインスピレーションを得たものである。
- Ring(リンク) はHTTPサービスを提供する方法であり、PythonのWSGIやRubyのRackに似ている。
- clj-record(リンク)はActiveRecordのようなORMマッパである。
- Lancet(リンク)はRakeに似たClojureのビルドツールで、作者はStuart Halloway氏である。
- clj-haml(リンク)はRubyのHAMLライブラリのポートである。
Rubyライブラリにインスピレーションを得たClojureライブラリは、この2言語を相互作用させる1つの方法である。別の方法がJRubyで、両言語は基礎をなすランタイムとして「JVM」を共有している。
Daniel Kwiecinski氏がブログの中で、JRubyとClojureを組み合わせるアイデアを探究し(リンク)、Clojureのデータ構造と機能をRubyオブジェクトとしてJRubyで利用可能にする実験を行っている。
この件でとりわけ興味深いのがClojureの永続データ構造で、たとえばその永続的なベクトル(Persistent Vectors)である。注意:ここでいう「永続的」は、データがディスクに存続することは意味しない。存続するデータ構造はイミュータブル(不変)であり、挿入のような修正は可能でも、常に完全なデータ構造の新しい「コピー」が作り出される。Clojureの実装と不変性の制約により、完全なディープコピーを行う必要性を回避でき、代わりに、比較的小さなデルタ(差分)のみをコピーすればよい。Karl Krukow氏がClojureの永続的なベクトルの実装を示し(リンク)、パフォーマンスのよい動作を実現する方法を説明している。
JRuby開発者にとってさらに好都合なのは、ClojureがSTMをサポートしていることであり、この概念を使って実験する方法をRuby開発者に提供する。Rubyのコードが明示的にClojure特有のSTM機能を使っていない場合でも、基礎をなすアプリケーションモデルをClojureで書くことは可能であり、Railsからのものであろうと、他のRubyフレームワークからのものであろうと、JRubyを前面に持ってくることができる。
さらに、Clojureはパフォーマンスのボトルネック対策に使うこともできる。JRubyのパフォーマンスは着実に良くなってきているが、(J)Rubyの多様性と柔軟性の代わりにパフォーマンスが欲しい、と思う状況がいまだに存在する。MRIでは、Rubyアプリケーションのパフォーマンスボトルネックは、ネイティブの拡張を書けば直すことができる。JRubyでは、解決策の1つはJavaでコードを書くことだが、それはJavaのソースコードかもしれないし、JVMのバイトコードを生成する方法かもしれない(RubyバイトコードDSLもしくは、Charles Nutters氏によるDubyのような言語のどちらかを使ったバイトコード生成(参考記事))。
低レベルの選択は、システム言語がCであっても(MRI向け)あるいはJavaであっても(JRuby向け)、明らかな欠点がある。Javaのレベルまで落ちてしまい、たとえばBlockや多くのタイプのメタプログラミングといった機能を失ってしまう。
Clojureがこの問題の解決策となり得る。Clojureには多様なレベルの柔軟性が用意されている。たとえば、Clojureの通常の関数は静的メソッドの呼び出しにほとんど限定されているため、呼び出しオーバーヘッドが低く抑えられる。Clojureは、たとえばマルチメソッド(リンク)という形で、ランタイムの多様性を様々な方法で提供する。
ClojureはLISPであり、強力なマクロシステムを備えている。マクロにより、コンパイル時のメタプログラミングが可能で、これにより、コンピュータへコード生成をオフロードする機会をさらに増やせる(ボイラプレートのコードや繰り返しのコードを手作業でJavaを使って記述しなくてもよくなる)。Clojureコードは常にJVMバイトコードにコンパイルされ、事前(AOT)コンパイルもサポートしている(リンク)。AzulのCliff Click氏が、ClojureやJRubyなどのJVM言語のパフォーマンスを詳しく調べ(リンク)、Clojureのパフォーマンスについての情報を提供している。全般的に見てClojureは、洗練されたスタイルで素速いコードを書くための機会を多数提供する。
反面にはもちろん、依存性の問題がある。Clojureを使うということは、プロジェクトにまた1つ、新しい依存性が追加されることを意味する。Clojureのような、まだほとんど知られていない言語をプロジェクトへ追加することがよいアイデアか否かは、すべてのチームが答えを見つけなければならない問題である。
並行処理ならびに共有データに対するSTMソリューションについては、広く議論されてきた。Clojureの制作者Rich Hickey氏は、AzulのCliff Click氏と、STMについて長時間議論した(リンク)。STMに関しては、昨年9月号のACMQueueに並行処理の情報 (リンク)が掲載されている。
みなさんの意見はどうだろうか。JRubyとClojureは好相性だろうか?