Lispectって名前のLispを作った話

2026-03-07 #プログラミング

Emacsに帰った

以前情熱駆動開発という記事の中で、長年連れ添ったEmacsからvscodeに移った話を書いた。(実はそのあとneovimに行ったりもした)

あれから1年。結論から言うとEmacsに帰った。

vscodeもneovimも良いエディタだ。AIとの連携も素晴らしかった。けれど1年使ってみて僕にとって「よくできた他人の家」だった。便利だけど自分の家じゃない。どこに何があるかわかるけど配置を変えたいとは思えない。

Emacsは違う。あれは僕の手そのものだ。.emacsを書くだけで世界が変わる。設定ファイルがElispで書かれているということは、エディタの挙動そのものをプログラミングできるということ。

帰ってきて最初にinit.elを開いた時の安心感は異常だった。

やはりLisp

Emacsに帰って改めて気づいたことがある。僕はLispが好きだ。

S式の美しさ。括弧の海。あの独特の見た目を嫌う人は多いけど、僕にとっては「全てが同じ構造で表現される」という事実が心地良い。データもコードも同じ形をしている。

マクロで構文レベルから言語を改変できる。普通の言語だと「言語が用意した構文に従う」しかないけど、Lispは「自分で構文を作る」ができる。これは最強としか言いようがない。

仕事ではRubyを書いている。けどなんか足りない。Lispなんだよな。

既存Lispに思うこと

じゃあLispでなんか作ればいいじゃんという話になるのだが、実際にプロダクトを作ろうとすると既存のLisp系言語にはそれぞれ好きじゃないところがある

Clojure

Clojureは素晴らしい言語だと思う。設計思想には深く共感するし、何よりElixirもかなり影響を受けてる。immutableがデフォルト、永続データ構造、STM。思想が美しい。

けどJVMが重い。起動に数秒かかる。メモリも食う。そしてこれが決定的なのだがCloudflare Workersで動かない。

個人開発でサーバーレスにデプロイしたいのにJVMが足枷になる。GraalVMでネイティブコンパイルする手もあるけど、それはもうClojureの良さを半分捨てているような気がしてならない。

Common Lisp

Common Lispは速い。SBCLのコンパイラは本当に速い。ネイティブバイナリも吐ける。

けどエコシステムが厳しい。Quicklispにあるライブラリの数はnpmの何百分の一だし、HTTPクライアントひとつ取ってもClojureと比べると選択肢が限られる。そしてこれもやっぱりCloudflare Workersでは動かない。

Elixir

Elixirは最も完璧に近い存在だと思っている。マクロがある、パイプ演算子がある、パターンマッチがある、OTPという強力な並行処理フレームワークがある。

本当に美しい。けどBEAM VMはJVMと同じ課題を抱えている。Cloudflare Workersでは動かないし、バイナリにコンパイルできない。

惜しい。本当に惜しい。

結局何が欲しいのか

散々文句を言ったので、じゃあお前は何が欲しいんだよという話をする。

  • Lispであること - S式、マクロ、これは譲れない
  • バイナリにコンパイルできること - 高速で動作する。起動が一瞬
  • WASMになること - Cloudflare Workersで動かしたい。ブラウザでも動かしたい
  • エコシステムが揃っていること - なんでも作れる程度にはライブラリが欲しい
  • 何らかの形で型を持っていること - 動的型付けだけだと大きくなった時に辛い
  • パイプ演算子 - |>がないと生きていけない体になってしまった

こんな言語、存在するわけがない。

存在するわけがないので、作るしかない。

Claude Codeなら作れるんじゃない?

以前の記事で情熱駆動開発について書いた。AIが僕の手足の延長になるという話だ。

Lispの処理系をゼロから書くのは大変だ。パーサー、評価器、マクロ展開、型チェック...。一人で全部やったら何ヶ月もかかる。

でも待ってくれ。僕の手足はもう伸びている。Claude Codeという最強の副操縦士がいる。

僕が「こういう言語が欲しい」と伝えて、設計の方向性を決めて、Claudeが実装する。僕が設計をレビューして、足りない部分を指摘して、Claudeが修正する。このループを回し続ければいけるんじゃないか?

いけた。

できたもの

数時間でできた。冗談みたいな話だけど本当に数時間で動くものができた。

lispectはLispコードをRustコードに変換するトランスパイラだ。S式で書いたコードがRustのコードになる。つまりRustのコンパイラでそのままコンパイルできる。

  • 漸進的型付け - 型を書きたい時は書ける。書かなくても動く。strict modeにすれば厳格にもなる
  • パイプ演算子対応 - (|> data transform1 transform2) はもちろん、thread-lastの (->>) もある
  • パターンマッチ - (match value (pattern result) ...) でenum分岐もできる
  • 文字列補間 - Rubyスタイルの文字列補間で式を埋め込める
  • deftype/defenum - 構造体と列挙型を定義できる
  • Rustのエコシステムにそのまま乗っかれる - crates.ioの全てが使える
  • Lispのマクロ - quasiquote対応のdefmacroで構文レベルの拡張が可能

Rustのエコシステムに乗っかれるというのが重要で(Goではありません)、HTTPサーバーが欲しければaxum、JSONが欲しければserde、DBが欲しければsqlxが使える。エコシステム問題が一発で解決した。

そしてRustにコンパイルされるということは、当然バイナリになる。WASMターゲットもRustの仕組みでいけるはずなので、Cloudflare Workersで動かすのも時間の問題だと思っている。

欲しかったもの、全部手に入った説がある(足りなかったclaude codeさんにお願いして増やしてもらおう)

26年二度目のブログリプレイス

理想のLispが目の前にある。使わないてはない。

このブログはもともとAstroで構築されていた。しかし、

Lispectで書き直してみたらファイルサイズが減った。全82記事のソースコードの総量が目に見えて減った。S式は冗長に見えるけど、実際にはHTMLタグの開始と終了を書く必要がないし、テンプレートの抽象化がマクロで自然にできるので無駄が少ない。

ちゃんと測ってないけど、Claude Codeに記事を書いてもらう時の出力トークン量も減ってそうな気がする。S式はLLMにとっても効率的な表現なんじゃないかと。括弧が明確に構造を示してくれるからパースしやすいんだと思う。

何より全てがLispで書かれているのが気持ちいい。テンプレートもLisp、記事もLisp、ビルドスクリプトもLisp。HTMLもCSSの参照も全部S式で表現されている。raw HTMLは一行もない。

美しい。

この先

Lispectはまだ生まれたばかりだ。今はこのブログの静的サイト生成にしか使っていない。

このあとはDBに繋いでみたい。sqlxをlispectから叩いてCRUDができれば、Webアプリケーションが作れるようになる。Rustのエコシステムが使えるのでやろうと思えば何でもできるはずだ。

publicにしたらまた記事を書くかもしれない。その時はもうちょっと技術的な話も書けると思う。