既存言語を採用してもフル機能は使わせられない
既存言語を採用する利点として挙がるのは、言語そのもの・処理系・エコシステム・学習資産がタダで手に入る、という話である。だがIaCの front end として採用する場合、手に入れた機能のかなりの部分を、こちらから禁止して回ることになる。
禁止する範囲は恣意的に決まるのではない。言語機能はグラフへ消える糖衣である限りで許されるをそのまま他人の言語に当てはめた結果として決まる。グラフ形状の決定位置に到達する高階関数、静的段階での I/O、停止しない再帰、eval、実行順に依存する可変状態。どれもホスト言語では正当な機能だが、蒸発しないので通せない。
サブセット化のコストは「別言語を作るのと同じ」では済まない
サブセット化が必要であること自体は既に書いた(全域性は後付けできない、「汎用言語 × 言語が決める」の空席は偶然ではない)。そこでの言い方は「ホスト言語をサブセット化するしかなく、それはもう別言語を作ることと同じである」だった。
これは入口のコストとしては正しいが、継続的なコストを勘定に入れていない。別言語を作るのと同じ、ではなく、別言語を作った上でホスト言語の面積も抱え続けるのが実際である。
- 利用者が読む言語仕様はホスト言語の全体のままで、こちらが尊重するのは部分集合だけ
- ホスト言語の型システムは、こちらが拒否したい構文も型検査に通す。禁止は言語の外側の層(lint、実行時チェック、規約)で表明することになる
- エコシステムはディスクの上にあり続ける。
npm installは動くが、入れたパッケージが静的段階で使える保証はない - ホスト言語が版を上げるたび、新機能が蒸発条件を満たすかを判定する仕事が増える
つまり「既存言語だから学習コストが低い」という利点は、サブセット化した時点で部分的に反転する。利用者は言語を知っていることによってむしろ、書けるはずのものが書けない理由を個別に学ぶ必要が出る。知らない言語を知らないまま学ぶより、知っている言語の禁止事項を覚える方が難しいことがある。
勘定の向きが逆のノートがある
「外部DSL × 規約に委ねる」が空席なのは淘汰圧の結果であるは同じ勘定を反対側から立てている。あちらは外部DSLを作るコスト(parser、evaluator、LSP、エコシステムの不在)を数え、その見返りが「文法レベルで禁止できること」なのだから、禁止を放棄した外部DSLには存在理由が残らない、と論じた。
外部DSLを作る
前払い
- parser / evaluator / LSPを書く
- エコシステムが無い
既存言語を採る
後払い — ホスト言語が生きている限り続く
- 禁止事項を言語の外側で表明し続ける
- 版が上がるたび新機能を判定する
本ノートはその対になる。既存言語を採る側にも、禁止を表明し続けるコストがかかる。両方を並べると、「作るか採るか」はコストの有無の対比ではなく、どちらのコストを払うかの選択になる。外部DSLは前払いで parser と evaluator を作り、埋め込みは後払いで禁止事項を維持し続ける。前者は一度作れば減っていくが、後者はホスト言語が生きている限り続く。
この論点が置き換わるもの
汎用言語埋め込みとの取引は構造的に対称、価値づけは非対称は、汎用言語の項ベース型付けを「持てない」ではなく「要らない」と評価した。本ノートはそれと別のことを言っている — 仮に要るとしても、採用したら禁止して回る側に立つ。「要らない」はドメインについての規範的判断であり、こちらは採用した場合に発生する構造的コストの話である。
したがって専用言語の選択理由として、「小さい言語で足りる」という積極的な理由(IaCが必要とする再利用は検証済みパターンのパラメータ付き合成であって任意計算ではない)とは別に、「大きい言語を採っても小さくして使うことになる」という消極的な理由が立つ。後者は前者が成り立たない場合でも生き残る。
根拠の確度
サブセット化が必要であることは既存ノートの通り。本ノートが足しているのは「サブセット化後もホスト言語の面積が残る」というコストの読みで、これは解釈である。実在ツールでこのコストがどう現れているか(Pulumi / CDKが実際に何を禁止し、どう表明しているか)は未確認。