世界を動かす技術を、日本語で。

約1ヶ月間Clojureを使った後の私の考え

12時間前原文(acdw.net)

概要

  • Clojure を使って静的サイトジェネレーターを開発した経験談
  • Common LispScheme と比較したClojureの利点を解説
  • データ構造や標準ライブラリの 実用性 を評価
  • Clojure の課題点や戸惑いについても言及
  • 今後もClojureを使い続ける意向を表明

Clojureを使った静的サイトジェネレーター開発の所感

  • Clojure でWebサイトを生成する静的サイトジェネレーターを自作
    • 新しい言語習得時の恒例プロジェクトとして実施
  • Common LispScheme と比較しながらClojureの特徴を分析

Clojureの気に入った点

  • Common Lisp よりも統一感のある言語設計
    • Common Lispは多数のLispの妥協の産物で、命名やパラダイムが一貫しない印象
    • Clojureは Rich Hickey 個人のビジョンが反映され、言語全体がまとまっている
    • seq抽象化 のおかげで、どのシーケンス型でも同じ関数(nthなど)が使える利便性
    • マッピングやイコール判定も統一された関数で対応可能
  • Scheme よりも“バッテリー同梱”の実用性
    • Schemeは仕様が小さく、機能が最小限(エラー処理やファイル操作、ハッシュマップも非搭載)
    • ClojureはJVM上で動作し、 標準ライブラリ やエコシステムが充実
    • 趣味プログラマーとしても使いやすい環境
  • データ構造 のエルゴノミクス
    • Lisp系の「すべてはリスト」思想が好きだが、実用上はベクタやハッシュマップも重要
    • Clojureはリスト・ベクタ・ハッシュマップ・セットを 第一級市民 として扱う
    • どの型もコア言語で平等にサポートされ、実用的

Clojureでつまずいた点

  • 構文(シンタックス)が多い
    • Lispの魅力はシンプルな括弧構文にあるが、Clojureは ()、[]、{}、#{} と多様
    • シンボル名の .や/ に特別な意味がある
    • アンコート(~)や`と,のシンメトリーが崩れている点が初見では違和感
    • ただし、慣れてくると使いやすさも実感
  • Java知識の不足
    • ClojureはJVM上で動作し、Javaとの連携(インタープ)が重要
    • Java自体には詳しくないが、Clojureの範囲内では大きな問題はなし
    • とはいえ、今後Javaエコシステムへの理解も深めたい気持ち

今後のClojure活用方針

  • Clojure は楽しく、使いやすい言語
  • babashka などのプロジェクトでスクリプト用途にも最適
  • Javaエコシステムの知見も積極的に吸収したい
  • 静的サイトジェネレーター以外にも、 Project Euler などでClojureの経験を拡張予定
    • コード共有用フレンドコード: 2304741_7MlSvxoUOKyle32IQjYszcAAsJDCVji1

Hackerたちの意見

機能的パラダイムは最初はちょっと不安だけど、問題解決が…違った感じになるよね。個人的には、大規模システム設計にはOOPが一番直感的だと思ってるけど、それは私の意見ね。Clojureではほとんどのモデルが特に良いパフォーマンスを発揮しないけど、OpenAIのモデルは言語の力をフルに活用してる。主観的には、なんか性格に合ってる気がするな。

これが正しく読めてるか分からないけど、OpenAIモデルの「成功率」テーブルではClojureが下の方にいるね。プロバイダーをAnthropicに切り替えると、Clojureを含むほとんどの言語の成功率が劇的に上がるよ。

リンクありがとう!Clojureに大規模なOOP設計に足りないものは何だと思う?私の理解では、ClojureはOOPをアラカルトで提供してるんだ。オブジェクト(マップ/レコード/構造体を通じて)、多相ディスパッチ(マルチメソッド/プロトコル/ケースを通じて)、型(Malli/TypedClojureを通じて)、継承(derived、isa?などを通じて)、ある程度のカプセル化(defn-/^:privateを通じて)…

生のモデルはClojureではTypeScriptやPythonほど効果的じゃないけど、Clojureには他の言語にはないスーパーパワーがあるんだ。それがREPL!特にnREPLとその周りのエコシステムね。LLMはフィードバックループが重要だから、もしLLMが自分で書いたコードを実際にテストできるなら、もっと効果的になるよ。静的型はフィードバックの一種だし(LSPを使えるなら)、ユニットテストや統合テストもそう。ClojureのREPLは特に優れてる。LLMはどんな関数のどんな部分でも評価できるし、知らない関数を試すこともできる。データを取得したり、異なる引数を試したり、いろんなアプローチを試してから一つに決めることができる。データベースをクエリしたり(もちろん読み取り専用接続)、結果を見たり、APIからデータを取得してそれを組み合わせたりもできる。実行中のプログラムにフックして内部からデバッグすることもできるんだ!これによって、訓練されていないライブラリやパラダイムを使うのがすごく効果的になるよ。私の経験では、ClojureのREPLにLLMを接続すると、もっと複雑なものが書けるようになる。エラーゼロで10倍複雑なプログラムが書ける感じ。だって、組み立てる前にちょっとずつ試せるから。まるで人間がプログラミングしてるみたい。でも、めっちゃ早い。ClojureとLLMの話になるとちょっと熱くなっちゃうけど、ほとんどの人がそのすごさに気づいてないと思うんだ。ほんとにクレイジーなことだよ。VSCodeを使えばすごく簡単だし、calva-backseat-driverっていう拡張があって、これが全部繋げてくれる。CopilotにREPLへのアクセスを与えて、ClaudeにもアクセスさせたいならMCPを公開すると思う。

私は大規模システム設計にはOOPが最も直感的だと思うけど、それは私だけかも。Clojureの美しさは、大きなプロジェクトの大部分を貫通するような変更をしたいときに際立つよ。可変データを使っていると、いろんなコードがオブジェクトを一貫性なく変更してしまって、バグがたくさん出ることがある。Clojureでは、誰かがデータを渡してきたら、オブジェクトを更新しても遠くのコードを壊すことは絶対にないんだ。なぜなら、常に速く更新されたコピーを作るだけだから。コードベースが複雑になるほど、この利点が実感できるよ。私はこれをRustの借用チェッカーと似たような結果を得るための簡単なメカニズムだと思ってる。データを所有するのは一つのコードだけだから、安全性が高いんだ。でも、個人的には使いやすいと思う。誰も何も所有していないって分かってるから、みんなが全てを読めるんだ。それに、コードをマルチスレッドに変換するのもすごく簡単で、いくつかの制約のもとで確実に正しいことが保証される。明確さとバグの減少、使えるコアが増えるために、いろんな機能がうまく組み合わさってる。

リッチ・ヒッキーの「Are We There Yet」は、ここにいる誰かにとって興味深いかもしれないよ。https://www.youtube.com/watch?v=ScEPu1cs4l0 私はこれが見た中で結構面白いトークの一つだと思った。あなたもそうだと思うけど、私は関数型言語が大好き。でも、どう表現すればいいのか分からない問題があって、関数型言語が「ただ動く」レベルに達するところで止まってしまう気がする。もしかしたら、私がバカすぎるだけかも。

私は大規模システム設計にはOOPが最も直感的だと思うけど、それは私だけかも。もっと慣れて、型付き関数型プログラミング言語を試したいなら、OCaml(古い技術が好きならSMLも)をチェックすることを強くおすすめするよ。モジュールファンクタがどのように適用されるかを見ると、いくつかファンクタを書いた後は、ほとんどのソフトウェアが非常に過剰設計に見えると思う。F#やGleamでコーディングしているときに一番恋しい機能だよ。

個人的には、大規模システム設計にはOOPが最も直感的だと思ってるけど、それはあくまで俺の意見。かつては俺もそうだった。でもClojureで関数型に移行してからは、戻ることは考えられないよ。マップを使って、データを別のデータに変換する共通の関数を持つのが確実にいい方法だね。これについては、ここで見てみて:https://youtu.be/aSEQfqNYNAc

関数型の方がずっと楽だな。プログラム全体に広がった状態をモデル化しようとしても、実際にそれを隔離したり、プログラムの小さな部分だけを考えたりする方法がないと、すごくストレスが溜まる。

今、Clojureでこのウェブサイトを生成してるよ。みんな知ってるけど、自分の静的サイトジェネレーターを書いたら本当のリスパーってわけさ。自分の「テンプレートエンジン」を追加するのがめっちゃ簡単で、全部マクロで実装したんだ。デメリットは、クラッシュがかなりきつかったこと。良い静的サイトジェネレーターには、出力の最適化、スコープ付きCSSのサポート、SPAフレームワークコンポーネントのサーバーサイドレンダリング、そしてもちろんNodeエコシステムとの統合が必要だからね(良くも悪くも、便利なものがたくさんある)。それ以来、Astroに移行したよ。でも、自分一人でSSGをここまで押し進められたのは本当に面白かった。

へへ、ヒックアップに触発されて、好きなClojureのテンプレートライブラリをNixで実装しちゃったよ!静的サイト生成のためにね :) それのデモもあるんだけど、基本的にヒックアップと同じように見えるし、動くよ。https://emsh.cat/niccup/examples/blog/

ブロックの終わりの ]}]})))}-ness に移動するのがもっと簡単だったらいいなと思う。これがどういう意味かよくわからないけど、全ての括弧と何が違うの?ちなみに、私はpareditを使ってて、)を押すとどんな括弧でも越えられるんだけど。それがなくても、左右の矢印キーを押せばいいだけじゃない?

その通り。Paredit最高!

敬意を表して言うけど、この話題はもう散々議論されてるよね。数年前にClojureを試したときは私も好きだった(合成やデータ構造には同意するよ;どちらも素晴らしい)。でも、本当に価値があるのはランタイムであって、構文じゃないんだ。Javaはしっかりしたランタイムを持ってるけど、Erlangのようにはまだ良くないし、Golangの基準にも達してないかも。ここで言ってるのは同時実行性や並列性のことね(メモリ管理に関してはJavaが非常に良いのは疑いようがない)。それに、グリーンスレッドのことも知ってるけど、ErlangやGolangができることができるようになったら、また真剣に考えるよ。プログラミング言語の構文はあまり重要じゃない。ある程度は重要だけど、プログラマーはそれを過剰にロマンチックに考えがちなんだ。ランタイムとその特性を最適化する方がずっと大事だよ。

同時実行性に関して、Golangのランタイムが特別にできることって何?私が試したときは、Cスタイルの構文が好きな人向けのErlangの劣化版に見えた。設計スペースによっては、広範な不変性が大きな利点になるけど、GolangにはそれがないけどClojureにはある。Erlangはもちろんそれ以上のものを持ってるし。clojerlがもっと普及すればいいなと思ってた。

うん、内容と雰囲気が2013年に読んでるみたいだった。別に悪いことじゃないし、新たに発見されるのは良いことだよね(失われたり忘れられたりするよりは)、でもちょっと笑顔になったよ。

JVMの仮想スレッドはGoの並行性に匹敵しないのかな?core.asyncも今は仮想スレッドを使ってると思うけど。

プログラミング言語の文法はほとんど重要じゃない。ある程度は重要だけど、プログラマーはそれを過剰にロマンチックに考えがちだよ。ランタイムとその特性を最適化する方がずっと良い。私はこの議論を理解しているか分からない。JavaとClojureは同じランタイムを共有しているけど、イディオマティックなJavaのコードベースはイディオマティックなClojureのコードベースとは非常に異なるアーキテクチャとデザインを持つことになる。逆に、Goで書かれたコードベースは、異なるランタイムを使っていてもJavaで書かれたコードベースに非常に似ていることがある。

Lisp Flavored Erlang(LFE)試したことある?俺はまだ試せてないんだ。数年間Elixirを使ってウェブバックエンドを作ってたけど、ほんとに楽しかったよ。LFEも試してみたいと思ってたけど、別の会社やスタックに移る前に結局できなかったな。

ErlangやGolangのランタイムは、JVMにはできないことが何があるの?

プログラミング言語の文法はあまり重要じゃない。でもClojureは文法以上のものを持ってるよ…デフォルトで全てのデータ構造を不変にするという意見があるんだ(構造的共有のように[1])。これはプログラムの設計やデバッグの仕方に大きな違いをもたらすよ。

Clojureは厳密で実用的な「不変性優先」のパラダイムがあって、他のプログラミング言語では得られないものなんだ。LISPは単なるランタイム文法以上のもので、独特の評価モデルやメタ言語的なコアを持ってる。ClojureがJVMを選んだのは、そのリーチと広大なエコシステムのためだよ。人々はClojureを他のランタイムにも移植していて、Beam(Clojerl)でもそこそこ成功してるみたいだね。

作者はSmalltalkに詳しいのかな?あれは文法がすごくシンプルなんだよね。Lispもある意味ではそうだけど、他の言語に比べて多いところもあるし、演算子と関数の考え方によるかな。

Clojureの文法と意味を学んだら、もうJVMに縛られなくなるよ。ClojureScript(JS)、ClojureCLR、ClojureDart、jank(C++)、Basilisp(Python)、babashka(SCI)など、いろんな選択肢があるからね。だから、Javaを知らないとかJVMが嫌いな人でも、自分が一番快適に感じる場所でClojureを使える可能性が高いよ。基本的に、ホストとの相互運用性を使わないClojureのコードは、どの方言でも動くし、現在の方言に応じて条件付きコードもサポートしてる。これがClojureのすごいところの一つだね。