「実行前に結果が読める」がなぜ根本価値なのか。そこから記述自由度の話へ降りていく。
planは中間表現で、applyはリンカである。この読み替えが実バグを予言するところまで。
値ベースの全数検査という強い性質から出発して、その検査が届かない場所を特定する。
意味的本体はグラフだけ、という立場から、高階関数を入れないという判断まで。
コードを読めば分かることと、moduleを使い慣れているから分かっていること。体感は同じでも、成果物に宿るか個人に宿るかで性質がまるで違う。
planはロックを取らず、serialとlineageの2つだけで「誰かが書いた」ことを事後に報告する。名前が付いている段と、実際に陳腐化を食い止めている段は別である。
失敗の悪さは損失の大きさではなく、気付けるかどうかで決まる。溢れた内容を無言で捨てる設計が最悪なのはそのため。
テンプレートがスキーマを兼ねる設計。同居できない言語境界では、片方を正として生成する。狙いはどちらも同じ。
タイトルのslugをキーにすると、誤字を直しただけで上書きが的を失う。IaCの論理IDと同じ問題の別領域での再演。
純粋性は意志の問題ではない。stateは本質的な必要物ではなく、出力先が非合成的であることへの補償である。
痕跡の不在が「やっていない」と同値でない時点で、観測は判定器になりえない。二度捕まえ損ねた実測記録。
差分は失うと戻らない。絶対状態を、一度ではなく毎回送ることで初めて収束する。宣言的であることの実務上の利得。
本体がシェルを知らないのは行儀ではなく、配布物にシェルが入っていないための必要条件である。
共変・反変・不変の三択は好みではない。型引数が出てくる位置にあるか、入っていく位置にあるかで決まってしまう。
List/Mapは全て共変で、Mapはキーも共変。だがそれを確定させたのは変性を決めるための検討ではなく、別目的の修正の副産物だった。
これらの探索から起票された、あるいは探索中に参照されたissueの一覧。各項目に「何のissueか」と「どの概念ノートに属するか」を付す。
公称識別の軸ごとの包摂は、設計としては方向付きである。受け側が特定している軸は送り側も一致していなければならず、受け側が特定していない軸は問わない。ところがその判定に使われている関数は対称な同値関係として書かれている。
目指す形は設計文書で決まっている。だが3か月弱を経て、コードは1つも動いていない。四つは四つのままである。
10変種を解決経路で並べると三群+誤りに割れる。二段階の間の窓に当たるのは上流参照系の3つだけで、1つは時間の向きが逆である。
入力が未知のとき、previewはコールバックに一歩も入らない。評価されるのは精度の落ちたプログラムではなく、長さの違うプログラムである。
段階を跨ぐ値(applyまで決まらない値)の扱いに、IaCツールの設計差が最も鮮明に現れる。
箱の比喩はIOや関数モナドで壊れる。実態は「次に何をするか」の繋ぎ方の共通形で、文脈固有のロジックはbindの定義1箇所に入る。
コードの整理が本番の作り直しになると、誰も整理しなくなる。stateだけを書き換える3つのブロックが、その結びつきを切っている。
繋ぐものが値か関数か。この一点が、planを表示してレビューできるかどうかを決めている。モナドは劣った構造ではなく、強すぎる。
「グラフだから使えない」は言い過ぎ。貰えるのは評価段階だけで、identity・差分・state・Unknownは残り、エッジの検査は言語境界で失われる。
既存言語をfront endに採ると、蒸発しない機能を禁止して回ることになる。「別言語を作るのと同じ」では済まず、作った上でホスト言語の面積も抱え続ける。
「planはコンパイルである」という類比が切れる場所は散らばっていない。すべて一つの原因に帰着する — ターゲットが受動的なメモリではなく、生きた外部世界であること。切れ目は三つある。
静的段階では値が決まらないものがある。上流スタックのapply結果への参照、まだ作られていないリソースの属性、for展開の変数などで、Terraformの「(known after apply)」に相当する。
Carinaのようなインフラ記述言語は「実行時に型を検査する動的型付き言語」ではないし、古典的な静的型付き言語でもない。静的段階と動的段階という二つの段階を持つ言語として読むのが正確である。
全域性は、それ自体が普遍的に望ましい性質なのではない。静的段階が何の門番をしているかによって、その価値が変わる。
静的段階の全域性は、実装を頑張れば手に入る種類の性質ではない。言語設計の選択そのものに由来するため、汎用言語に埋め込む方式を選んだ時点で原理的に取れなくなる。
carina planは、ソース言語(.crn)のプログラムを、検査を通しながら中間言語(Effectのリスト + 依存DAG)へ翻訳するコンパイラである。applyはその中間言語の実行系である。
中間表現としてのplanは、コンパイラのIRが持つ古典的な性質をいくつも備えている。単一定義性、直列化可能性、そして「検査済みであること」の三つである。
Carinaの処理系は「処理が進むと値の型が変わる」typestateパターンを系統的に使っている。狙いは一貫していて、静的段階の内部不変条件を、実装言語の型でコンパイル時に固定することである。
汎用言語埋め込みと専用全域言語の取引は、構造としてはきれいに相補的である。
IaC言語に機能を足してよいかどうかには、はっきりした判定条件がある。言語機能は、静的段階の終わりまでにグラフへ正規化されて消える糖衣である限りで許容される。以後これを蒸発条件と呼ぶ。
静的段階の出力であるplanは、Read / Create / Update / Delete / Import / Remove / Move / Wait / DeferredCreate / DeferredReplaceの10種のEffect値の列である。
IaCの差分エンジンがやっていることは、コンパイラの命令選択(instruction selection)の変種として読める。命令セットは Effect の10種で、Replaceはその中に無い — 置換はCreate/Deleteの対へ展開される複合命令である。
IaCが本当に必要とする再利用は、検証済みパターンのパラメータ付き合成であって、任意計算ではない。それはモジュール・for・exportsという有界な合成手段 — すべて蒸発条件を満たす — で足りる。
代入可能性判定の最終アームは、二つの型の表示用の名前を文字列として比較するものになっている。
このノートは腐る。 行番号は当時のリポジトリのスナップショットであり、対象リポジトリが更新されれば合わなくなる。
これらの探索で答えが出なかった問いの一覧。概念ノートではなく、次に調べるべきことのリストである。既に解決したものは各概念ノートに事実として書かれているのでここには含まない。
identityとは「何を同一資源と見なすか」の規則である。stateをどこに置き誰が運用責任を負うかという問い(章4の主題)とは別の独立軸であり、同じツールでも別々に設計できる。
構成記述系DSLを「何を出力するか」で分けると、3自由度では捉えきれない別レイヤの差が浮上する。
記述自由度の二層は、逃げ道の有無という一点でトレードオフを作る。
記述言語と記述自由度の所在の2×2で、「外部DSL × 規約に委ねる」— 動的なsourceと任意関数を持つ専用DSL — に該当するツールがない。これは偶然の空白ではなく淘汰圧の結果である。
「汎用言語 × 言語が決める」象限に該当するIaCツールは、確認できる範囲で存在しない。これは偶然ではなく、ホスト言語をサブセット化しない限り埋まらないという構造から来る。
IaCのコンパイルは二引数plan(source, world)である。このうちworldの取得が遅い。
planはロックを取らず、applyがロック下で差分を再計算する。この契約を意識的に推すと、plan時のworldの鮮度は正しさの問題ではなくUXの問題だと正当化できる。
worldを変更イベントで維持される実体化ビューにするという構図は、データベースの増分ビュー維持(incremental view maintenance)そのものである。
worldを速くする三つの攻め口のうち、最も踏み込んだものは「読まなくてよい条件を作る」である。自分しか書かないリソースについては、自分の記録が真であり、クラウドAPIを叩く必要がない。
CPUはメモリの全走査をしない。ページテーブルのdirty bitが「変わったページ」だけを教え、キャッシュ一貫性プロトコル(MESI系)は無効化通知を受けたキャッシュ行だけを再取得する。
world取得を速くする三つの攻め口を一つの設計に落とすと、三層になる。
「一度検査したのだから、あとは信用してよい」は、データが同じ信頼領域に留まっている間だけ成り立つ。
Carinaの型検査は、項(構文)にラベルを付ける体系ではなく、評価済みの値がその型に属するかを判定する様式を取る。これができるのは静的段階が全域的だからで、値ベースの検査様式は停止性の配当として読める。
CarinaのUnion(Vec<AttributeType>)はタグなし合併(untagged union)である。「いずれかのメンバが受理すれば妥当」という集合論的合併の意味論を持つ。これには理論的な代償がある。
WASMプラグイン境界(WITプロトコル)では、型情報の一部が不可逆に消える。
差分計算の中核にあるのは、等価性が型ごとに定義される(type-directed equality)という原則である。同じ値の組でも、期待される型によって判定が変わる。
IaC言語の型検査が項ベースの型体系をほぼ必要としないのは、実装上の手抜きではなく、意味的本体がグラフだけであることの帰結である。型を付けるべき「プログラム」が実質存在しないので、検査対象はグラフそのものになる。
Carinaには型を表す言語が二つある。ユーザーが書く型注釈の言語(TypeExpr)と、プロバイダのスキーマが表すスキーマ型(AttributeType)である。
設定記述言語の系譜は「汎用言語に埋め込むか、専用言語を作るか」の一本の線ではない。「専用言語かどうか」と「全域的かどうか」は独立した選択であり、その二軸が張る空間として見た方が正確である。
Unknownの素通しは「未解決の値の周りは無検査」という意味ではない。値が無くても宣言型どうしを照合する別経路の検査が走るため、静的近似が本当に確定できない窓は見かけより狭い。
「静的段階の評価が必ず停止する」という性質は、一般再帰を禁じてループを有限に限るだけでは出てこない。構文制限と参照グラフの非循環性強制という二本の柱で初めて成り立つ。
静的検査器が二系統ある — plan/validate経路とLSP経路 — というのは、機能の重複ではなく理論的な負債である。同じ検査の二重実装であり、意味論の一致(パリティ)を規約と試験で維持している。
Carinaの型付けの様式は、型を「項に付く構文的な札」ではなく「値の集合(値の分類器)」とみなし、型付けを所属判定として与える意味論的型付け(semantic typing)の系譜である。
「well-typedなIRだけがバックエンドに渡る」というのがコンパイラの標準的な契約である。しかしapplyの再配置はIRに新しい値を書き込む。
Carinaに「カスタム型」という独立した型構成子はない。代わりにString / Int / Floatの各変種が制約を持ち運ぶ。
「この属性は誰が書くのか」— ユーザー指定か、プロバイダ算出か、サーバ既定か — という区別が、Carinaでは型に乗っていない。フラグと投影で表現されている。これが理論的な負債になっている。
Unknownの扱いは、検査では楽観的に、等価性では悲観的に振る舞う。方向が逆に見えるが、どちらも「インフラを壊さない側」に倒すという一つの原則の現れである。
列挙やカスタム文字列型にはTypeIdentity { provider, segments, kind }という公称的な識別が付く(providerは省略可能)。
CloudFormationのWAFv2 Statement → AndStatement → List<Statement> のような循環スキーマを表すため、スキーマは
未知値をめぐる三すくみは、構造の言葉で言い直せる。鍵になる区別は、デプロイ計画が「平らな宣言の集まり」か「継続のつながり」かである。
planを中間表現として読んだときの最も特徴的な性質はこれである。このIRはユーザーインターフェースである。
IaC言語の意味的本体は、リソースの定義と、その依存関係グラフである。それ以外のすべての言語機能は、あれば便利という程度のものにすぎない。
「動的段階では、コードに書いたこと以外の副作用が発生しない」という性質を動的段階の効果の閉包性と呼ぶと、これは二つの独立した命題に分解できる。
汎用言語埋め込み型のIaCツールが得るパッケージエコシステムには、他のドメインでは生じない種類のコストがある。
通常のコンパイラはソースだけの関数である: compile(source) = binary。IaCのコンパイルは二引数である: plan(source, state) = patch。
値ベース検査の売りは「プログラムを完全評価してから、出てきた値を全数検査する」ことである。しかしこの「全数」性には時刻の指標が付く。検査されるのは、検証ステップの時点で値が確定しているものだけである。
インフラ記述の主要な読者はレビューアと将来の運用者である。彼らが問うのは「このコードは何を計算するか」ではなく「この記述から実際に何が作られるか」の判読性である。
applyの仕事をコンパイラの言葉で言うと、リンクとロードである。plan中の穴を実値で埋めながら、依存順に実行していく操作だからである。
類比の価値は、記述が整うことにあるのではない。対応から実装上の予言が導け、その予言が実際のバグや改善方向を当てることにある。当たらない類比は装飾であり、当たる類比は分析の道具である。
代入可能性の判定は、手書きの分岐の集合として書かれている。個々のアームは妥当だが、それらが全体として部分型関係の性質を満たすかはコードでも試験でも保証されていない。反射性・推移性・反対称性のいずれもである。
構造体の値検証は、必須フィールドの存在を確かめ、未知のフィールドを拒否する(似た名前があれば候補として提示する)。つまり幅部分型付け(width subtyping)を負の方向にしか許さない。
「記述言語(外部DSL / 汎用言語)× 記述自由度の所在(言語が決める / 規約に委ねる)」の2×2は、暗黙に「ツール = 1象限」を前提していた。
記述自由度は無数に立てられるが、「実行前にコードを読んだ時点で、resource graphの形と各ノードの属性値がどう決まるかを読み取れるか」に直接効く軸に絞ると、三つになる。
stateをめぐる設計判断は一つの軸ではなく二つある。
PulumiのOutput<T>やCDKのToken / BucketPropsのように、SDK側が型を提供して依存追跡や未知値伝播を表現するパターンがある。これを「言語が決める」象限の実例と数えるのは過大評価である。
コード構造を変えたとき(rename / move / refactor)に既存リソースが破壊されないかどうかは、identityの生成規則そのものではなく、旧identityと新identityを歴史的に繋ぐ機構がある…
IaC 4ツールと構成記述系DSL 11本を、3自由度 — 抽象化の追加 / 複数量の制御 / 実装の切替 — で横断評価した一覧表。
4ツール(Terraform / CDK / Pulumi / Carina)を軸ごとに横断比較した概観表を集約したもの。(要確認)と書かれたセルは埋まっていないまま残してある — 何が確認できていないかも情報である。
構成記述系DSL 11本(Pkl / Bicep / KCL / CUE / Dhall / Jsonnet / Nickel / cdktf / cdk8s / Crossplane / Helm)を、同じ枠組み —…
2026-04-27時点のスナップショット。 carina-rs/carina(https://github.com/carina-rs/carina) repoをIaCツールとして必要な20項目で点検した結果である。
「DSLだから読みやすい」のではなく、「『言語が決める』範囲が広いから(= 規約に委ねる範囲が狭いから)読みやすい」。読みやすさの原因は記述言語の選択ではなく、記述自由度の所在にある。
予測可能性の基準を「実行前にコードを読んだ時点でresource graphの形と各ノードの属性値を読み取れるか」と置くとき、暗黙に読む主体は人間が前提されていた。
IaCの根本価値は「apply前に結果を見せる」ことにある。だから予測可能性 — 実行前にコードを読んだ時点でresource graphの形と各ノードの属性値を読み取れること — は、他の設計軸と並ぶ一つの軸ではなく、…
論理ID / URN / binding名はstate/IR内部の識別子であり、AWS上の物理リソース名(S3 bucket名、IAM Role名等)とは別物である。
ユーザーの記述自由度をどこまで許すかという設計判断は、誰が制限するかで二層に分かれる。
「言語が決める」か「規約に委ねる」かという二択の外側に、拡張ポイントを言語境界で開けるという第三の選択肢がある。
構成記述系DSL 11個を3自由度で点検すると、当初の4象限には現れていなかった第5象限が浮上する。Pkl / KCL / Dhall / Jsonnet / Nickelの5つが、独立に同じパターンへ収束している。
ハイブリッド象限への合流は構造的整合性の議論であって、ユーザの実需要とは別の問題である。実需要の観点から見ると、IaCで高階関数は「あれば嬉しいが必須ではない」機能かもしれない。
コードから読み取りたいものは二つある。graph形状(どんなリソースがいくつ、どう繋がって作られるか)と、各ノードの属性値(そのリソースが具体的にどう設定されるか)である。三つの記述自由度はどちらの側にも適用される。
graph出力DSLに高階関数を入れる核心は属性値計算の自由度を上げることにあるが、その自由度がgraph形状側(resource生成位置 / module選択位置 / identity決定位置)に染み出すと、「実装の切…
予測可能性はgraph形状と属性値の両方に効く。運用の現場では「このリソースは具体的にどう設定されているか」をコードから知りたい場面 — インシデント対応、コンプライアンス監査、セキュリティレビュー、IAMポリシー検査、…
「IaCで高階関数は本当に必要か」という問いは、AI時代の観点から見直すと必要性が上がるのではなく下がる方向に傾く。IaCで実際に必要とされる三つの抽象化のいずれも、AIが肩代わりできるからである。
巨大なIaCリポジトリでは、AIが全コードを一度にコンテキストへ入れられない。このとき効くのは「局所読みでgraph形状と属性値が分かる」という性質であり、これは人の局所読解性と同質の制約である。
記述自由度から導かれる予測可能性の議論は、すべて「抽象化を経由していない範囲」に限定される。
これらのノートは確定した結論ではない。 すべて生成AI(Claude / Codex)との対話による設計探索の記録を分解したもので、結論の確定ではなく論点の整理と根拠確認を目的として書かれた作業ノートが元になっている。
「プロバイダのプロトコルで型が潰れるから型を強くできない」というのは、差の所在を取り違えた説明である。TerraformとCarinaを並べると、値を運ぶ層はどちらも型タグなしで送っている。
「Terraformの限界はHCLに起因する」という仮説は部分的に正しいが不十分である。限界は単一原因ではなく、出自の違う四つの層が重なっている。層を分けないと、解消手段の見積もりを丸ごと間違える。