プログラミング言語

Clojureとは?JVMで動くLisp系関数型言語の文法と環境構築・採用判断

Clojureとは?JVMで動くLisp系関数型言語の文法と環境構築・採用判断

Clojureは、JVM上で動作するLisp系の関数型プログラミング言語です。2007年にRich Hickeyが公開し、不変データ構造と並行処理の扱いやすさを設計の中心に据えています。本稿のコードはすべてClojure 1.12.6 と Temurin JDK 25.0.4.1(macOS・x86_64)で実行した結果を基に、主要な返り値や出力を読みやすく整形して掲載しています。バージョン番号・実測値は2026年9月時点のものです。

まとめ:Clojureの要点

  • 正体:JVM上で動くLisp方言。動的型付けで、括弧による前置記法を使う。Javaのライブラリをそのまま呼べる。
  • 最新安定版は 1.12.6(2026年9月2日公開)。1.13は2026年9月時点でalpha段階(1.13.0-alpha7)。
  • 1.12の目玉はJava相互運用の記法拡張。Integer/parseInt のようなメソッド参照を値として関数に渡せるようになった(1.11ではコンパイルエラー)。
  • データは既定で不変。assoc は元を書き換えず新しい値を返すが、内部構造を共有するため10万要素のベクターでも全複製は起きない。
  • 本稿のWeb例は Ring + Selmer によるHTML生成。Ringのハンドラは「マップを受け取りマップを返す関数」にすぎず、Selmerは既定でHTMLを自動エスケープする。
  • 弱点は起動時間。同一マシンで Hello World の実行が Clojure 約1.8秒に対し素のJavaは約0.23秒。処理ごとにプロセスを起動し、短い応答時間が必要な用途では起動コストを評価する必要がある。

Clojureの位置づけ:JVM上のLisp方言

「クロージャ(closure)」とは別物

カタカナで「クロージャー」と検索すると、Clojureと、関数が外側の変数への束縛を保持する仕組みである closure(クロージャ)が混ざって出てきます。両者は異なる概念ですが、Clojureにもクロージャを作る機能があります。Clojureは固有の言語名で、綴りは Closure ではなく Clojure で、s ではなく j が入ります。closure と書き間違えないよう注意してください。本稿は言語としてのClojureを扱います。

JVM上でのJava API呼び出しと実行基盤の制約

ClojureはJVMのバイトコードにコンパイルされて動くため、Javaのクラスをそのまま呼べます。新しい日付APIを使う場合もライブラリの追加は不要です。

;; JavaのAPIをそのまま呼ぶ
(.toUpperCase "clojure")
;;=> "CLOJURE"

(str (java.time.LocalDate/of 2026 9 21))
;;=> "2026-09-21"

(System/getProperty "java.version")
;;=> "25.0.4.1"

裏返すと、JVMの制約もそのまま引き継ぎます。メモリ構成・GC・JITの挙動はClojure固有の話ではないため、詳細はJava仮想マシン(JVM)とは?仕組み・メモリ構成・JITを実機の出力で解説を参照してください。同じくJVM上で動く簡潔な言語としてはGroovyとは?JVMで動く簡潔なスクリプト言語の文法・用途・採用判断が比較対象になります。

基本構文:括弧・defn・スレッディングマクロ

関数定義と呼び出し

Clojureの呼び出しや制御構造は、リストの先頭に関数・マクロ・特殊形式などを置く前置記法で表します。次の例の if は関数でもマクロでもなく、特殊形式です。(関数 引数 引数) の順で、Javaの f(a, b) の括弧が左へずれた形だと考えると読みやすくなります。

(defn greet [name]
  (str "こんにちは、" name "さん"))

(greet "佐藤")
;;=> "こんにちは、佐藤さん"

;; 条件分岐も同じ形
(if false "管理者" "一般")
;;=> "一般"

-> と ->> の読み分け

Clojureのコードを読むうえで最初の壁になるのがスレッディングマクロです。処理を重ねると括弧が内側へ深くなり、実行順と読む順が逆になります。これを上から下へ並べ替えるのが -> と ->> です。

;; ネストした書き方(内側から実行される)
(reduce + (map #(* % %) (filter even? (range 1 11))))
;;=> 220

;; ->> で上から下へ並べ替える。結果は同じ
(->> (range 1 11)
     (filter even?)
     (map #(* % %))
     (reduce +))
;;=> 220

2つの違いは値を差し込む位置だけです。-> は各フォームの第1引数に、->> は最終引数に差し込みます。Clojureのコア関数は、コレクションを最後の引数に取るもの(map・filter・reduce)と、対象を最初に取るもの(assoc・update・文字列関数)に分かれるため、この使い分けが必要になります。

;; -> は第1引数へ差し込む(文字列・マップ向き)
(-> "a,b,c" (clojure.string/split #",") first)
;;=> "a"

;; ->> は最終引数へ差し込む(シーケンス向き)
(->> [1 2 3] (map inc) (into []))
;;=> [2 3 4]

不変データ構造と状態管理

assoc による不変更新と構造共有

Clojureのマップ・ベクター・セットは既定で不変です。更新に見える操作は、元の値を残したまま新しい値を返します。

(def m {:name "田中" :role "dev"})
(def m2 (assoc m :role "lead"))

m   ;;=> {:name "田中", :role "dev"}
m2  ;;=> {:name "田中", :role "lead"}

「毎回コピーするなら重いのでは」という疑問が当然出ますが、実際には全体を複製していません。永続データ構造は変更のない部分を共有します。10万要素のベクターの先頭だけを差し替えて、末尾の要素が同一オブジェクトのままかを identical? で確認できます。ただし、これは末尾の要素オブジェクトが同一であることの確認であり、ベクター内部の木構造の共有や、全要素参照のコピーがないことを証明するものではありません。

(def big (vec (range 100000)))
(def big2 (assoc big 0 :changed))

(first big)   ;;=> 0
(first big2)  ;;=> :changed
(identical? (nth big 99999) (nth big2 99999))
;;=> true   ; 末尾の要素オブジェクトは作り直されていない

atom による状態管理と更新の再試行

実務では状態の更新が必要です。Clojureはそれを言語全体に散らさず、atom のような参照型へ明示的に閉じ込めます。swap! は関数を受け取り、競合時は自動で再試行します。

(def counter (atom 0))
(dotimes [_ 1000] (swap! counter inc))
@counter
;;=> 1000

この「値は不変・変化は参照型に限定」という分け方が、Clojureが並行処理に強いとされる根拠です。関数型プログラミング一般の考え方は関数型プログラミングの基本概念とその重要性で整理しています。

Clojure 1.12で変わったJava相互運用

1.12系で入った変更のうち、既存のコードの書き方に直接効くのがメソッド値(method values)です。従来、JavaのメソッドをClojureの関数として渡すには無名関数で包む必要がありました。1.12ではメソッドをそのまま値として渡せます。公式のリリースノートは「Programmers can now use qualified methods as ordinary functions in value contexts」と説明しており、ラッパー関数はコンパイラが生成します。

;; 1.11以前の書き方(1.12でも有効)
(map #(Integer/parseInt %) ["1" "2" "3"])
;;=> (1 2 3)

;; 1.12:静的メソッドを値として渡せる
(map Integer/parseInt ["1" "2" "3"])
;;=> (1 2 3)

;; 1.12:インスタンスメソッドは Class/.method
(String/.toUpperCase "abc")
;;=> "ABC"

(map String/.length ["ab" "abcd"])
;;=> (2 4)

これが1.12で追加されたものであることは、同じコードを古い版で実行すると確認できます。1.11.4で (map Integer/parseInt ["1" "2"]) を実行すると、次のコンパイルエラーになります。

Syntax error compiling at (m11.clj:1:38).
Unable to find static field: parseInt in class java.lang.Integer

1.11以前は Integer/parseInt という表記を「静的フィールドの参照」として解釈するため、メソッドを見つけられません。1.12より前のバージョンを対象にした入門記事では、この記法が紹介されていないことがあります。メソッド値を使うコードにはClojure 1.12以降が必要です。ただし、従来の記法も引き続き有効なので、メソッド値を使っていないことだけで記事が古いとは判断できません。

開発環境:Clojure CLIとdeps.edn

deps.ednによる依存ライブラリの指定

本稿で使う開発環境は公式のClojure CLI(clj / clojure コマンド)と deps.edn です。プロジェクト直下に deps.edn を置き、依存を Maven 座標で書きます。

{:deps {org.clojure/clojure {:mvn/version "1.12.6"}
        selmer/selmer      {:mvn/version "1.13.5"}
        ring/ring-core     {:mvn/version "1.15.5"}}}

版の確認元はClojure公式のダウンロードページと各配布元(Clojars・Maven Central)です。clj を引数なしで起動すればREPLが立ち上がります。REPLに式を送りながら書き進める「REPL駆動開発」がClojureの標準的な進め方で、関数単位で即座に結果を確認できます。

ビルドツールとエディタ

Leiningenは置き換えられた古い道具として紹介されることがありますが、2026年9月時点でも開発は続いており、最新版は2.13.0(2026年6月8日)です。既存プロジェクトがLeiningenなら移行を急ぐ理由はなく、新規はCLI + deps.ednで始めるのが無難です。なお正規のリポジトリはGitHubからCodebergへ移っており、GitHub側は「Moved to Codeberg; this is a temporary convenience mirror」と表示される暫定ミラーです。Issueを追うときは移転先を見てください。

対象 2026年9月時点の版 備考
Clojure 1.12.6 2026-09-02公開/1.13は alpha7
ClojureScript 1.12.145 JavaScript出力
Clojure CLI 1.12.5.1664 2026-07-27
Leiningen 2.13.0 2026-06-08
Selmer 1.13.5 テンプレート
Ring 1.15.5 ring-core
core.async 1.9.865 1.10系は alpha

エディタはVS Code + Calva、IntelliJ + Cursive、Emacs + CIDERが主な選択肢で、いずれもREPL接続が前提です。エディタ側の選定やAIアシスタントの併用についてはECA – Editor Code Assistant をさわってみる: Clojure製エディタ非依存AIアシスタントの初体験レビューで扱っています。

Ring + SelmerによるWebアプリのHTML生成

Ringのハンドラは「ただの関数」

ClojureのWeb開発はRing仕様が土台です。フレームワークらしい基底クラスや設定ファイルは登場せず、リクエストのマップを受け取ってレスポンスのマップを返す関数がハンドラです。テストもその関数を直接呼ぶだけで済みます。

SelmerによるテンプレートのHTML生成

SelmerはDjangoのテンプレート記法に沿ったテンプレートエンジンで、{{変数}} と {% タグ %} を使います。次はRingハンドラの中でSelmerを呼ぶ例です。末尾のコメントでは、返されたマップを項目別に分けて示しています。

(require '[selmer.parser :as parser])

(defn handler [request]
  {:status 200
   :headers {"Content-Type" "text/html; charset=utf-8"}
   :body (parser/render
           "<h1>{{title}}</h1><ul>{% for p in posts %}<li>{{p}}</li>{% endfor %}</ul>"
           {:title "記事一覧" :posts ["Clojure入門" "REPL駆動開発"]})})

(handler {:request-method :get :uri "/"})
;;=> :status  200
;;   :headers {"Content-Type" "text/html; charset=utf-8"}
;;   :body    "<h1>記事一覧</h1><ul><li>Clojure入門</li><li>REPL駆動開発</li></ul>"

自動エスケープと未定義変数の扱い

Selmerを使ううえで先に知っておくべき挙動が2つあります。変数展開は既定でHTMLエスケープされることと、未定義の変数はエラーにならず空文字になることです。実行して確かめた結果が次のとおりです。

(parser/render "{{comment}}" {:comment "<script>alert(1)</script>"})
;;=> "&lt;script&gt;alert(1)&lt;/script&gt;"   ; 既定でエスケープされる

(parser/render "{{comment|safe}}" {:comment "<b>太字</b>"})
;;=> "<b>太字</b>"                      ; safe フィルタで生のまま出る

(parser/render "値:{{missing}}" {})
;;=> "値:"                                ; 未定義変数は空文字

この例のようにHTMLのテキストとして挿入する場合、入力中のタグはエスケープされます。ただしこれはHTMLテキスト文脈の話で、JavaScript・URL・イベント属性へ埋め込む場合はそれぞれに応じた対策が別途必要です。自動エスケープだけでXSSを防げるわけではありません。|safe を付けた箇所はエスケープが外れ、無害化の責任が書き手に移るため、外部入力に |safe を使わない方針がそのまま防御になります。

未定義変数の扱いも把握しておく必要があります。既定では変数名を誤っても例外にならず空欄になるため、テンプレート側の綴り誤りは実行しても表面化しません。selmer.util/set-missing-value-formatter! で未定義時の出力を差し替えられるので、開発時だけ例外を投げる設定にしておくと早く気づけます。

Clojureを選ぶべきでない場面

採用前に確認したいのは、起動時間の許容範囲と、保守・レビューを担える体制です。

起動時間が課題になる用途には向きません。同一マシン・同一JDK(Temurin 25.0.4.1)で、Clojureは clojure.main からソースを実行し、Javaはjavac済みのクラスを実行しました。プロセス開始から終了までを time -p で3回ずつ測った結果が次の表です。

実行内容 1回目 2回目 3回目
Clojure 1.12.6 1.83秒 1.77秒 1.74秒
素のJava(javac済み) 0.23秒 0.23秒 0.22秒

この測定条件では、Clojureの実行開始から終了までの時間はJavaより約1.5秒長くなりました。ただし、初期化・ソースの読み込み・コンパイルなどを個別に計測していないため、差の内訳は特定できません。長時間稼働するサーバーでは起動コストを償却しやすいものの、再起動やスケールアウト時には影響が残ります。また、コマンドを叩くたびにプロセスが起動するCLIツールや、コールドスタートが課金・レイテンシに直結する短命なサーバーレス関数では、この差がそのまま体感の悪化になります。この用途に限れば、GraalVMのネイティブイメージ化やBabashkaのような別ランタイムを検討するか、素直に別言語を選ぶほうが早く済みます。

採用と引き継ぎの制約も現実的な論点です。Clojureの経験者は、JavaやKotlinの経験者に比べて母数が小さくなります。Clojureを書ける人が1人だけで、引き継ぎ資料や代替の担当者もいない場合、その人の離脱で保守が滞るリスクがあります。チームに関数型とLispの両方を学ぶ意思があるか、レビューできる人が2人以上確保できるかを先に確認してください。

どの領域で使われているかは公式調査が参考になります。State of Clojure 2025は「Fintech, Enterprise Software, and Healthcare are the top industries for Clojure at over 51% combined」と報告しており、採用が集中しているのは金融・業務システム・ヘルスケアです。ただし、この調査の業種別回答比率だけでは、自社の領域における事例数や採用候補者の見つけやすさまでは判断できません。

逆に、データ変換のパイプラインが処理の中心にあり、状態の共有と並行処理がバグの温床になっている領域では、不変データ構造とREPLでの逐次確認が効きます。既存のJava資産を捨てずに部分的に導入できるため、まずは独立したバッチやデータ加工から試すのが現実的な入り口です。

よくある質問

Clojureとクロージャ(closure)は同じものですか?

別物です。Clojureは2007年に公開されたJVM上のプログラミング言語の名前で、closure(クロージャ)は関数が外側の変数を保持する仕組みを指す一般用語です。綴りも Clojure と closure で異なります。

Clojureの最新バージョンはいくつですか?

2026年9月時点の最新安定版は1.12.6です。次期版の1.13は 1.13.0-alpha7 が公開されている開発段階で、本番利用には安定版の1.12.6を使います。

ClojureとClojureScriptの違いは何ですか?

言語としてはほぼ同じで、出力先が異なります。ClojureはJVMのバイトコードに、ClojureScript(最新版1.12.145)はJavaScriptにコンパイルされ、ブラウザやNode.jsで動きます。Javaのクラスを直接呼べるのはClojure側だけです。

-> と ->> はどう使い分けますか?

差し込む位置で選びます。-> は各フォームの第1引数に、->> は最終引数に前の結果を差し込みます。コレクションを最後に取る map・filter・reduce を並べるときは ->>、対象を最初に取る assoc・update や文字列関数を並べるときは -> を使います。

Clojure開発はVS Codeだけで足りますか?

足ります。VS CodeとCalvaでREPL接続・式の評価・補完を利用できます。ローカルで本稿のコードを動かすには、JDKとClojureの実行環境も必要です。IntelliJ + CursiveやEmacs + CIDERも同等の機能を持つため、普段使いのエディタに拡張を入れる方針で問題ありません。

関連記事

お気に入りに入れた記事の一覧

この記事は以下の記事からリンクされています

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.09.28 テックブログ タイムズカーの不正アクセスと約660万件の流出|免許証画像を退会者まで残さない保管設計
  2. 2026.09.25 コラム 最低賃金引き上げ【令和8年度】47都道府県の改定額・発効日と企業の対応手順
  3. 2026.09.25 コラム 障害者雇用の助成金一覧:月いくら・支給要件と申請書類を勤怠データで揃える方法
  4. 2026.09.28 テックブログ anthropic skillsとは?公式19スキルの中身とClaude Code・APIでの導入手順
  5. 2026.09.05 コラム 犯罪収益移転防止法の本人確認:2027年4月の対面IC読み取り義務化と改修要件

RELATED POSTS 関連記事

目次