この記事は Nix アドベントカレンダー 2025 の1日目の記事である。
## Nixのコードを読んでも意図はわからない
Nixは宣言的なパラダイムであり、一つ一つのコードは読めば何をやっているのか大体わかるようになっている。 そしてそれをどうやって実現するかは隠蔽されている。 つまりWhatやHowはただ設定を書くだけで説明できるのであるが、なぜその設定をしたのか、 なぜ他の選択を取らなかったのかといったWhyの情報は意識して書かないと残らない。
私は現在およそ10台ほどのコンピュータをNixにより管理している (https://github.com/natsukium/dotfiles) が、 4,5年かけて無造作に設定を継ぎ足していった結果、同じような設定が複数の箇所に散らばっていたり、 コード全体に一貫性がない状態となってしまった。 コードを一つ一つ読んでいけば何をやっているかを把握するのは難しくないのだが、なぜここだけ微妙に設定が異なるのか、 なぜここにこの設定を配置しているのか、関連する設定はどれなのかといったコード全体の背景や意図が 曖昧となってしまっている。 その結果、整理しようとしてもうまく整理できず中途半端になり、よりコードベースとしてぐちゃぐちゃとしていった。
## 文芸的プログラミングで意思決定を残す
部分的な意図や説明であれば、単純にコードへのコメントを残すだけで十分であろう。 実際先述した私の dotfiles にはなるべく標準的でない設定やワークアラウンドに対して コメントを残すように心がけている。 こうすることで個々の設定はたしかに意図が残るのだが、やはりコード全体の、各マシン間の繋がりや モジュール同士の関連を記すには心もとないように感じる。
そこでこの問題を解決する手段として文芸的プログラミングについて考えたい。 文芸的プログラミングはプログラムの中に補助的に記していくコメントという立場を逆にしたようなもので、 ここではドキュメントの中にプログラムの断片を記述していくスタイルのようなものだと考えてもらえれば良い。
文芸的プログラミングにおいて、現代でもっとも柔軟でどの言語にも使えるツールは Emacs で使われている Org だろう。
他にも markdown で同様のことができるツールであったり、 Emacs 以外で Org を編集できるソフトウェアなど いくつかあるが、コード抽出の柔軟性だったり、編集体験といった機能面で Org に匹敵するものは 2025 年時点で存在しないと考える。
## 書いたドキュメントをWeb上で公開する
org は GitHub などのホスティングサービス上で markdown などと同じようにレンダリングされるが、 Emacs を用いると Org を HTML など他の形式に変換できる。 つまり例えば GitHub Pages などで以下のように静的ファイルとしてホストすることができる。
https://dotfiles.natsukium.com/
## i18n で多くのユーザーにリーチする
Nixは欧州/米国を中心に数多くのユーザーを抱えており、英語で情報を残すべき。 しかしながら日本でももっと普及してもらいたいため可能であれば手間を掛けずに 日英のドキュメントを生成したい。 Nixのコードに詳細なコメントを残すだけではなかなか翻訳ツールの恩恵を受けることができず、 コードも煩雑で見にくいものとなってしまうだろう。 一方、ドキュメントには古くからi18nのツールが数多くあり、中でも po4a は枯れて安定した技術だ。 文芸的プログラミング、特に org で書かれたものはこうしたツールと相性が良く、 ドキュメントとコードを分離することなく、それでいて原文と翻訳を分けて管理することができる。
そしてここで生成されたファイルももちろんWebで公開することができる。
https://dotfiles.natsukium.com/ja
## orgを書くのにEmacsは必要か
ここまで org を使った文芸的プログラミングを紹介したが、実はわたしは普段全く Emacs を使っていない。 正直ほとんど操作の仕方もわからないし、Nixのコードを生成するコマンドでさえも調べながらでないと実行できない。 しかしこれらはコマンドラインから実行できるし、 org ファイル自体は今どき LLM が生成してくれる。 Emacs も org も歴史が長いので LLM にとって馴染み深く書きやすいのは大きな利点だ。 つまりちょっと org を書いてそこから Nix やその他のファイルを生成するのにエディタとしての Emacs はもはや必要ないと言える。
一方、ドキュメントのツールチェインとしては重要である。 例えば私は Makefile に以下のような定義を追加している。
# org-babel tangle
EMACS := emacs --batch -l org --eval '(setq org-src-preserve-indentation t)'
define tangle-org
$(EMACS) --eval '(org-babel-tangle-file "$(1)")'
endef
tangle: overlays/default.nix
overlays/default.nix: overlays/configuration.org
$(call tangle-org,$<)
https://github.com/natsukium/dotfiles/blob/81ac9b642fb42b60c5116078ca59343f705b921d/Makefile#L3-L8 https://github.com/natsukium/dotfiles/blob/81ac9b642fb42b60c5116078ca59343f705b921d/Makefile#L49-L60
こうすることによってちょっとした修正は普段使い慣れたエディタ上で行い、プログラムを生成することはコマンドの実行に任せることができる。
しかしもちろん使えるようになったほうがよいのは当然である。 例えば、現代の開発においてもはや LSP による支援は必須であるが、文芸的プログラミングは地の文のなかに コードブロックとしてコードを書いていく以上かなり LSP との相性が悪い。 しかし Emacs で人気の LSP クライアントである lsp-mode には 実験的なものではあるが org で LSP を動かす機能が実装されている。 https://emacs-lsp.github.io/lsp-mode/manual-language-docs/lsp-org/
他にも org 自体の記述をサポートするコマンドが Emacs 本体に多数含まれており、 本格的に運用していくなら覚えて損はないと考えている。
## ドキュメントとコードの乖離を防ぐ
文芸的プログラミングはドキュメントとコードが乖離しにくいことが利点ではあるが、 ドキュメントからプログラムを抽出するという作業を挟む以上、抽出を忘れて古いプログラムのまま過ごしてしまう可能性も存在する。 さらに i18n の節で紹介した po4a はコードが埋め込まれたドキュメントから翻訳対象のテキストのみを抽出し翻訳ファイルとして管理する。 つまり、このワークフローでは文芸的プログラミングの利点を享受するために、2箇所のコード生成という苦痛を伴う。 しかし私たちには心強い味方、 git-hooks.nix がついている。 例えばコミット前にプログラムの抽出を行っているかを確認するには以下のようなフックを書けばよい。
checkScript = pkgs.writeShellScript "check-org-tangle" ''
set -euo pipefail
${pkgs.gnumake}/bin/make -B tangle
# Check for differences using git diff
changed=$(${pkgs.git}/bin/git diff --name-only)
if [ -n "$changed" ]; then
echo "Org files were out of sync and have been auto-tangled."
echo "Changed files:"
echo "$changed"
echo ""
echo "Please stage the changes and commit again:"
echo " git add $changed"
exit 1
fi
exit 0
'';
これにより、ドキュメントをコミットした時点で必ずコードと翻訳ファイルが同期されていることをシステムで保証できる。
## 終わりに
実際に文芸的プログラミングを実行するには複雑なツールの使い方を覚える必要があり非常にハードルが高い。 しかし、強い再現性をもち宣言的にマシンを構成できる Nix のコードに、その設定の意図を含めることには大きな価値があると考えている。 書いた本人もなぜその選択をしたのか、なぜ複雑なワークアラウンドをやっているのか、時間が経とうと理解することができるし、 同じ悩みを抱えた他者もその思考の過程を追うことができる。 私が愛用しているツール一覧といったブログの代わりにもなるし、ホームサーバーの紹介も簡単にできる。 何より常に現在使っているツールの紹介がその設定や他のツールとの組み合わせまで含めてできるというのは価値があるだろう。