Clojure

Clojure (発音は/'klouʒər/, クロージャー)はプログラミング言語であり、LISP系の言語の方言の一つである。関数型プログラミングのプログラミングスタイルでのインタラクティブな開発を支援し、マルチスレッドプログラムの開発を容易化する汎用言語である。Clojure言語のプログラムはJava仮想マシンとMicrosoft .NET 共通言語ランタイムで動作する。Clojure言語は「データとしてのプログラムコード」 (英語:「code as data」) という思想で設計されており、洗練されたマクロ機構を持つ。 リッチ・ヒッキー (Rich Hickey)がClojure言語を設計した目的は、既存のJavaプラットフォーム上で動作して、並行コンピューティングができる、関数型のLISP系の言語を作ることである。 Clojure言語が並行コンピューティングを実現する手法は、不変(イミュータブル)な状態の連鎖という概念によって特徴づけられる。 状態が不変であるため、ひとつの状態に対して複数の操作を並列に行うことができ、並列性という問題が「状態遷移の管理」になる。そのため、Clojure言語には、状態遷移に関して明確な定義をもつ可変な参照型がいくつか用意されている。 他のLISP系言語と同様、Clojure言語のプログラムはS式で表現する。プログラムコードはコンパイルされる前に、リーダーによって解析され、内部データ構造に変換される。Clojure言語のリーダーは、リストの他に配列・ハッシュテーブル・集合もリテラル表現として扱うことができ、そのままコンパイラに渡される。言いかえると、Clojureのコンパイラはリストだけでなく、上に挙げた全てのデータ構造を直接扱うことができるということである。Clojure言語はLisp-1であり、関数名と変数名は同一名前空間にある。また、他のLISP系言語とのコードの互換性は考慮されていない。 Clojure言語のマクロ機構は、Common Lispのそれによく似ている。ただし、Clojure言語のバッククオート(「シンタクス・クオート」と呼ばれる)では、個々の記号が局所的な名前空間によって区別されるという点で、Common Lispのマクロとは異なっている。この仕組みによって、マクロ展開時の変数の捕獲(= マクロが展開された環境に同名の変数があると、その変数の値が変更されてしまうこと)を避けている。マクロ展開時の変数の捕獲を許容するように強制することもできるが、それは明示的に行わなければならない。また、Clojure言語では、現在の名前空間にインポートされた別の名前空間の大域名を変更することは許容されていない。 対話型評価環境(REPL)による動的な開発ができる。 関数が第一級のオブジェクトであり、副作用に基いたループを使わずに、再帰呼び出しを動作原理とするものであること。 遅延シーケンス (英語: lazy sequence) イミュータブルで永続なたくさんのデータ構造 ソフトウェアトランザクショナルメモリ、agentシステム、動的varシステムを使った並行コンピューティング Java仮想マシンのバイトコードを生成するコンパイラ言語であること。 マルチメソッドにより、複数の引数の型の組み合わせで実行される処理内容を変更することができる。(普通のオブジェクト指向言語のポリモーフィズムでは、ただ1つの引数によって決定される) Javaとの密接な統合: Java仮想マシンのバイトコードにコンパイルされるため、Clojure言語で作られたアプリケーションソフトは、Javaの既存のシステムを生かすことができる。そのため、アプリケーションのパッケージ化やアプリケーションサーバーへの配備も容易に行える。また、この言語のマクロ機構を使えば、既存の Java API の呼び出しも容易に行える。さらに、Clojure言語のデータ構造はいずれも、Java の標準的なインターフェースを実装しているので、Java言語の側からClojure言語のプログラムを呼び出すのも簡単である。 CUI版のHello World: (println "Hello, world!") GUI版の Hello World: (javax.swing.JOptionPane/showMessageDialog nil "Hello World") 重複しない通し番号の、スレッドセーフな生成器 (let [i (atom 0)] (defn generate-unique-id "Returns a distinct numeric ID for each call." [] (swap! i inc))) ダミーのjava.io.Writerのサブクラスを無名クラスとして作成し、それを使って、内部のprint関数からの全ての出力を無効にするようなマクロを定義。 (def bit-bucket-writer (proxy [java.io.Writer] [] (write [buf] nil) (close [] nil) (flush [] nil))) (defmacro noprint "Evaluates the given expressions with all printing to *out* silenced." [& forms] `(binding [*out* bit-bucket-writer] ~@forms)) (noprint (println "Hello, nobody!")) それぞれに10個の値の入った長さ100の配列(vector)があり、10×100＝1,000個の異なる連続した数が格納されている。これら100個のvectorを16個のスレッドで共有する。各スレッドは、ランダムに2つのvectorを選びそれぞれの配列でランダムな位置を決めて、二つの配列の要素の内容を交換する、という作業をClojure言語のソフトウェアトランザクショナルメモリを使って行う。この作業を300,000回繰り返しても、最初に格納されていた1,000個の数はどれひとつとして失われることがない。 (defn run [nvecs nitems nthreads niters] (let [vec-refs (vec (map (comp ref vec) (partition nitems (range (* nvecs nitems))))) swap #(let [v1 (rand-int nvecs) v2 (rand-int nvecs) i1 (rand-int nitems) i2 (rand-int nitems)] (dosync (let [temp (nth @(vec-refs v1) i1)] (alter (vec-refs v1) assoc i1 (nth @(vec-refs v2) i2)) (alter (vec-refs v2) assoc i2 temp)))) report #(do (prn (map deref vec-refs)) (println "Distinct:" (count (distinct (apply concat (map deref vec-refs))))))] (report) (dorun (apply pcalls (repeat nthreads #(dotimes [_ niters] (swap))))) (report))) (run 100 10 16 300000)