2009年4月29日水曜日

Clojureの紹介

Clojureの特徴。

-JavaVMの上で動く
-Lispの新しい方言
-動的型付けの純粋でない関数型言語
-STMによる並行処理
-豊富でimmutableなデータ型
-遅延シーケンス

というところあたり。

1.JavaVMの上で動く
JavaVMの上で動く言語は、ScalaやGroovy、ABCLなど
たくさんある。

これらの中でClojureは新しいほう。
JavaVMの上での言語開発の利点は、Javaとの連携が容易で
Javaの豊富なライブラリを利用できる点と、
JVMの高いパフォーマンス、マルチプラットフォーム対応
ガーベージコレクタなどの恩恵を受けられる点など。

2.Lispの新しい方言

Common LispやR5RSなどの仕様によらない、
新しく設計されたLispの方言。
当然コードはS式でマクロが使える。
新しいLispという意味では、ポールグレアムのArcなどと同様ですね。
JVM上のLispは、Common Lisp on JVMのABCL、
Scheme on JVM のSISCやKAWAなどがあるが、
新規設計のLisp方言onJVMはClojure以外にいまのところなさそう。
リーダマクロが独自定義できない、
末尾再帰最適化がないなどの理由から、
Common Lispなどからきたユーザには窮屈に感じられそう。

Lisp以外からきたユーザには、Common LispやSchemeより
逆にとっつきが良いかもしれない。

主観的な感想だが、
Common Lispの無骨な感じや歴史からくる複雑さ、
Schemeの原理主義的な感じから比べて、
Clojureには、PythonやRubyのような
ユーザに優しい雰囲気が感じられる。

3.動的型付けの純粋でない関数型言語
Haskellなどのような純粋関数型ではないが、
immutableなデータ型とそれへの参照を使って状態が管理されるので、
Python,Common Lisp などに比べると、
比較的破壊的な操作が少ない。

基本はimmutableで、プロセスディクショナリやetsを持っている
Erlangとポジションは似ているかも。

ここまでは前提条件的なもの。
ここからが、とくにClojureの魅力的な点。

4.STMによる並行処理

STMは、Software Transactional Memory
wikipedia:ソフトウェアトランザクショナルメモリ
複数スレッドからの共有メモリの読み書きを、
データベースのトランザクション処理のように制御する方法。
"Beautiful concurrency" Simon Peyton Jones (PDF)
や、書籍「ビューティフルコード」など。
並行制御として、ErlangやScalaのようなアクターモデルと
このSTMと、あとはロックを使う方法などがあって、
どれが有用なのかについては、議論の的になっているようだ。

5.豊富でimmutableなデータ型

データ型の便利さにPythonに近いものを感じる。
Erlangの持つimmutableでファーストクラスのコンテナは、
リストとタプルしかない。
dictはリストの組み合わせでしかなく、リレラルもない。
これはErlangの弱点だと思われる。
また、immutableなコンテナは、パフォーマンスの低下を
招きかねないが、そこらへんも効率よく実装されているらしい。

6.遅延シーケンス
Clojureはもちろん遅延評価ではないが、
遅延シーケンスが言語レベルでサポートされている。
Pythonのイテレータやジェネレータの感覚にとても近いものを感じるが、
遅延シーケンス自体もimmutableなので、状態を内在している
Pythonのジェネレータより扱いやすい点もありそう。
ここは要調査。

Clojureのlistとvector初見

Clojureは便利な組み込みのデータ型を備えている。
これらはぜんぶが全部immutable、変更不可能になっている。
でも参照を扱う仕組みが別にあって、参照先が変更可能なので、
ErlangやHaskellより命令的に破壊的な操作?を書ける。
そこはSTMと絡んでくるので後回し。

Clojureは動的型で、Pythonの組み込み関数typeと同じように
型について調べる事ができる。
user> (type 1)
java.lang.Integer

整数や文字列などはJavaのクラスがそのまま使われているようだ。
JavaのVMの仕組みはわからないけれど、
Javaは静的型なので、こういったインスタンスやクラスの情報は
なんらかの形でラップされて扱われているんだろうと思われる。
型もふつうのオブジェクトのようで、比較ができる。
user> (= (type 1) (type 3))
true
user> (= (type 1) (type 30000000000000000000000))
false

typeのtype、Pythonだとメタクラスになるところだが、
おなじようなものだろう。
user> (type (type 1))
java.lang.Class
user> (type (type (type 1)))
java.lang.Class

文字列や、大きい整数もこんな具合。
user> (type "abc")
java.lang.String
user> (type 10000000000000)
java.lang.Long
user> (type 100000000000000000000000000)
java.math.BigInteger

コンテナ型は、Clojure独自に定義されている。
immutableということで、PersistentHoge
という名前が付けられている。
user> (type [])
clojure.lang.PersistentVector

これはPersistentVector。単にvectorと呼ばれることの方が多い。
IPersistentVectorというのは明らかにインターフェースと思われるが
MultiMethodsと関係してくるのだろうか?今は不明。

user> (type ())
clojure.lang.PersistentList$EmptyList

こっちはPersistentList。EmptyListとついているのが気になる。
これがうわさのMetadataか?
listとvectorが別にある。vectorの方が可変長配列でlistはリンクリスト
ってことだろうか。
listは当然だがvectorにもリテラルが用意されている。
user> [1 2 3 4]
[1 2 3 4]
user> [1, 2, 3, 4]
[1 2 3 4]

カンマは、あっても無くても同じ。
好き嫌いが分かれそうだが、僕は嫌いじゃない。
関数で生成することもできる。
user> (vector 1 2 3 4)
[1 2 3 4]
user> (vector)
[]

listのほうはこんなかんじ。
user> '(1 2 3 4)
(1 2 3 4)
user> '(1, 2, 3, 4)
(1 2 3 4)
user> (list 1 2 3 4)
(1 2 3 4)

Lispですね。

Clojureのインストール(Ubuntu 9.04)

Clojure on Ubuntu
を参考にUbuntu 9.04にClojureをインストール。
sudo apt-get install openjdk-6-jdk
sudo apt-get install ant subversion git-core
mkdir ~/opt
cd ~/opt
svn co http://clojure.googlecode.com/svn/trunk clojure
svn up
cd clojure
ant
mkdir ~/.clojure
cp clojure.jar ~/.clojure
cd ~/.clojure
java -cp clojure.jar clojure.lang.Repl
user=> (+ 1 2)
3
cd ~/opt
git clone git://github.com/kevinoneill/clojure-contrib.git
cd clojure-contrib
git pull
ant -Dclojure.jar=../clojure/clojure.jar
cp *.jar ~/.clojure
emacs ~/.profile
=============
export CLOJURE_EXT=~/.clojure
PATH=$PATH:~/opt/clojure-contrib/launchers/bash
alias clj=clj-env-dir
=============

user=> (System/getProperty "java.class.path")
"/home/xxx/.clojure/clojure.jar
:/home/xxx/.clojure/clojure-contrib-slim.jar
:/home/xxx/.clojure/clojure-contrib.jar"