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

HNに表示: GoでClojure風の言語を作成しました、起動時間は7msです

概要

  • let-go はGoで書かれたClojure方言の言語
  • JVM不要・単一10MBバイナリ・7ms高速起動
  • Clojureコードの約95%互換(jank-langテスト基準)
  • CLI・スクリプト・Webサーバ・WASM・Go組み込み用途に最適
  • JVM完全互換やJAR読込は非対応、用途特化型言語

let-go概要

  • let-go は、 Go で実装された Clojure方言 および バイトコードコンパイラ+スタックVM
  • 単一10MBバイナリ で配布、 JVM不要約7msのコールドスタート
  • Clojure の約 95%互換 (jank-langテストスイート基準)
  • 2021年に「 Goを書いているフリをしながらClojureを書く」というジョークから開発開始
  • CLIツール、スクリプト、Webサーバ、daemonlessコンテナランタイム などで実用
  • スタンドアロンバイナリ自己完結型WASMページ へのコンパイル対応
  • Plan 9 でも動作確認済み
  • JVM Clojureの完全な代替 ではなく、 JAR読込非対応 ・一部ライブラリ利用時は調整が必要

主要な特徴・目標

  • Clojureの主要機能 (永続データ構造、遅延シーケンス、トランスデューサ、プロトコル、レコード、マルチメソッド、core.async、BigIntなど)を実装
  • Goとの双方向インタープリタ (関数・構造体・チャンネルの相互利用)
  • AOTコンパイル によるバイトコード生成・スタンドアロンバイナリ作成
  • WASMページへのコンパイル (ターミナルエミュレーション付き)
  • nREPLサーバ 搭載(Calva, CIDER, Conjure等に対応)
  • Babashka Pod 互換で豊富なPodエコシステムを活用可能
  • コンテナランタイムやCLI、Webサーバ、Go組み込みスクリプト層 としての利用

ベンチマーク・比較

  • バイナリサイズ :let-go 10MB(最小)、Babashka 68MB、Clojure JVM 304MB
  • 起動速度 :let-go 6.7ms(Babashkaの約3倍、JVMの約50倍高速)
  • アイドル時メモリ :let-go 13.5MB(最小)
  • 短命なデータ処理 (map/filter/persistent map等)で高速
  • 数値演算系の大規模ワークロード ではgo-jokerやJVMが優位
  • Babashkaと同等性能 のアルゴリズムベンチマーク多数
  • Joker(上流)比で10倍以上高速

互換性・サポート状況

  • jank-lang/clojure-test-suite95.4% のアサーション合格
  • clojure.core, clojure.string, clojure.set, clojure.walk, clojure.edn, clojure.pprint, clojure.test, core.async など主要名前空間をカバー
  • Babashka Pods のロード対応(SQLite, AWS, Docker等のPod利用可)
  • ~/.babashka/pods/ を共用し、bbでPodインストール後let-goから利用

既知の制限事項

  • 未実装 :Refs/STM, Agents, 階層(derive等)、chunked sequence、タグ付きリテラル、deftype/reify、Spec、alter-var-root
  • 数値のオーバーフロー検出非対応 (int64でサイレントラップ、BigInt演算子は別途用意)
  • 一部動作の差異 :concat*はeager、Goチャンネルは常にブロック、数値タワーはint64/float64/BigIntのみ、正規表現はGo(re2)準拠

実例・利用例

  • xsofy :同一ソースでブラウザ・ターミナル両対応のローグライク
  • lgcr :syscall名前空間を活用したdaemonlessコンテナランタイム
  • examples/ :小規模サンプル
  • test/ :.lgテストファイル

インストール・利用方法

  • Homebrew対応 (macOS/Linux):brew tap nooga/let-gobrew install let-go
  • バイナリ配布 :Linux, macOS, Windows(amd64/arm64)向け
  • Go 1.22+でソースビルドgo install github.com/nooga/let-go@latest
  • 基本コマンド
    • lg:REPL起動
    • lg -e '(+ 1 1)':式評価
    • lg myfile.lg:ファイル実行
    • lg -r myfile.lg:ファイル実行後REPL
    • lg -c app.lgb app.lg:バイトコードへコンパイル
    • lg -b myapp app.lg:自己完結バイナリ作成
    • lg -w site app.lg:WASM Webアプリ化

WASM・Webアプリ対応

  • 自己完結型index.html (~6MB, WASM内包, gzip圧縮)
  • xterm.js連携 でフルターミナルエミュレーション(ANSIカラー、カーソル、キーボード入力)
  • COOP/COEPヘッダ 付与用Service Worker同梱(GitHub Pages対応)

nREPLサーバ

  • Emacs(CIDER)M-x cider-connect-cljで接続
  • VS Code(Calva) :プロジェクトREPL起動または直接接続
  • Neovim(Conjure).nrepl-port存在時に自動接続
  • 対応ops :clone, close, eval, load-file, describe, completions, info, lookup, ls-sessions, interrupt

Goプログラムへの組み込み

  • Goからlet-go VMを起動・関数/構造体/チャンネルを渡してスクリプト実行
  • Go側の構造体→let-goレコード化、Goチャンネル→let-goチャンネル化
  • Go関数→let-goから呼び出し可能
  • 詳細例pkg/api/interop_test.goにサンプル多数

テスト・開発

  • テスト実行go test ./... -count=1 -timeout 30s
  • GitHubページ/リポジトリ : https://github.com/nooga/let-go
  • 作者の他プロジェクト :paserati(Go製TypeScriptランタイム、20MBバイナリ)

まとめ・補足

  • let-goピュアGo実装のClojureライク言語、JVM不要・高速起動・小型バイナリ
  • nREPL・WASM・Go連携 ・Podエコシステム活用など多機能
  • 既存Clojureプロジェクトの完全移植は非推奨、用途特化・新規開発やCLI/スクリプト・組み込み利用に最適
  • コミュニティ参加・PR歓迎、気軽に試用・フィードバック推奨

Hackerたちの意見

これがずっと探してたClojureのポートだ!主に、Goのコアライブラリとチャネルの抽象化が、コアと非同期APIと一緒に使えるシンプルで素敵な基本APIに到達してると思ったから。大きくて美しいバイナリの欲求も満たしてくれるしね。作業ありがとう!CPPへの新たな愛が落ち着いたら、またチェックするつもり。

優しい言葉ありがとう!最終的にやるときは、ぜひ問題を一つか二つ教えてね :)

Goのランタイムとパッケージを活用した「オールドスクールPHP」スタイルのDSLを作るアイデアを考えてたんだ。オールドスクールPHPって言うのは、PHPがかつてはウェブに特化したDSLだったからで、今はそうじゃないよね。PHPに似た使いやすいバックエンド言語ができると思うけど、Goのフルパワーがあるから面白いと思う。Clojureは素晴らしい選択だね。

このWasmブラウザREPLを試してみて!https://gloathub.org/repl/ GloatはGlojureのAOT自動化ツールだよ。去年の夏にJames Hamlinと一緒にGlojure AOTを進めて、それ以来ずっと進めてるんだ。marcingas(nooga)とも協力して、Gloat/Glojure/let-goが一緒に動くようにしてるよ。

https://github.com/chr15m/awesome-clojure-likesへのPR大歓迎だよ!

https://github.com/chr15m/awesome-clojure-likes/pull/27を追加したよ。教えてくれてありがとう :)

いいね!最近、GoのセマンティクスにLispの構文を試してみたんだ:https://codeberg.org/veqq/Joe JVMなしのClojureっぽいものなら、Janetが本当にいいよ。しばらくの間、プロダクションで使ってる:https://janet-lang.org/ LuaのVMとライブラリが欲しいならFennelもあるよ。

ありがとう!Joeは良さそうだね!Janetについては、自分では試したことないけど、Clojureになろうとしてるんじゃなくて、自分の道を行ってると思ってた。

マイクロな指摘だけど、7msのコールドスタートって書いてあって、その少し下に6msってあるね。READMEを読むと速くなるのかな?

修正したよ、ありがとう!俺のマシンでは6-7msだね。中央値は約6.5msみたい :)

代わりにJokerっていうのもあるよ: https://joker-lang.org これ、すごくいいと思うし、全然評価されてないよね :)

Jokerのいいところは、Goのライブラリをスムーズにラップしてくれるところだね。Goが提供するものは全部カバーしてるみたい。だけど、あんまり追加しすぎないようにしてる。let-goが10MBに収まるのが好きなんだ :)

もっといい名前があればいいのに、「lets-go」だけじゃなくて。例えば「clogo」とかどう?

名前を付けるのは難しいけど、let-goは好きだな。層のあるダジャレだし :) (let [...] (go ...))Goに入れることができるんだよ!clogoはちょっと無理かな。他にいいアイデアある?良いのがあれば考えてみるよ :)

最近、Goにコンパイルするプロジェクトが増えてる気がする。これって、Goのランタイムや標準ライブラリがすごく優れてるってことを示してると思う(GD'dルートを選ぶときに)。でも、表面的な部分(構文)はシンプルすぎて冗長で、開発者が求める表現力が足りないんだよね。今のところ、Lisette(http://lisette.run)が一番良くてアクティブなGoにコンパイルする言語みたい。

それ、結構良さそうだね。ただ、PascalCaseとcamelCase、snake_caseの混合はちょっと好きじゃないな。

Joker、Janet、Glojure、そして今Let-go。何か見落としてる?

Cで作られてるのがあるよ。http://mino-lang.org/

JVMはマイクロ秒で立ち上がるよ。ちょっと調整が必要だけどね。

JVM自体はそうだけど、Clojure自体を待たなきゃいけないんだ。Clojureは、いろいろなものをインターンして、起動するたびにvarの参照を計算するから、遅いことで有名なんだよね。let-goをレーシングカーにするつもりはなかったけど、小さなディストロで、メモリ使用量が少なくて、サクッと立ち上がるのが今の優先事項だよ。