notes mizzy.org

構成記述系DSL 11本の調査

確認済み 出典: explorations/2026-04-26-cross-dsl-design-position/tier-1〜3各ファイル 作成

構成記述系DSL 11本(Pkl / Bicep / KCL / CUE / Dhall / Jsonnet / Nickel / cdktf / cdk8s / Crossplane / Helm)を、同じ枠組み — 記述言語の位置づけ / 抽象化機構 / 3自由度の評価 / 2×2配置 / 設計判断 — で観察した調査データ。調査日は全DSL共通で2026-04-26、公式ドキュメントの当時の記述に基づく。

3自由度は「抽象化の追加」「複数量の制御」「実装の切替」で、各自由度を「言語が決める」か「規約に委ねる」かで判定する。

Pkl (Apple)

公式: https://pkl-lang.org/main/current/language-reference/index.html

記述言語の位置づけ:外部DSL。Appleが公開した構成記述専用言語で、独自パーサと評価器を持つ。評価は純粋(副作用なし。例外は read() による外部リソース読み込み)、late-bound(object propertiesは遅延評価)。文法はJava/Scala系の見た目で、module, class, function, let 等の予約語を持つ。

抽象化機構:

単位構文役割
moduleファイル単位(.pkl)設定単位 / 再利用単位
classclass Foo extends Bar { ... }単一継承の型定義
functionfunction greet(b: Bird): String = "Hello, \(b.name)!"値の計算抽象
objectnew { ... } / amends { ... }レコード値、プロトタイプ的な拡張可

module組み立ては import "uri"(値として読み込み)、amends "uri"(ベースにして上書き拡張)、extends "uri"(クラス的に継承)の三つ。

3自由度:

2×2配置:外部DSL × 言語が決める、ただし抽象化の追加だけは「規約に委ねる」寄り。

設計判断: 「外部DSLを選びつつ、構成データ計算の表現力(関数 / 高階関数 / 標準ライブラリ)は汎用言語に近づける」ハイブリッド。最重要の「実装の切替」だけを厳格にロックし、出力されるオブジェクトツリーの構成が静的解析だけで決まる。「複数量の制御」を標準ライブラリに集約する設計は、評価器の表現力を絞って停止性を確保する効果を持つ。得たものは構成データの最終形が静的に決まる安心感と純粋性による再現性、失ったものは「実行時条件で別moduleを選ぶ」抽象化(if env == "prod" then import "prod.pkl" else import "dev.pkl" は不可)。

Bicep (Microsoft)

公式: https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/file

記述言語の位置づけ:外部DSL。Azure専用で、コンパイルするとARM template (JSON)を生成する。2段階評価(Bicepファイル → ARM template → Azure Deploymentがapply)、宣言的(公式: "the order of elements doesn't affect how deployment is processed")、副作用なし。

抽象化機構:

単位構文役割
Bicep file.bicep ファイルデプロイ単位 / module単位
resourceresource <name> '<type>@<version>' = { ... }個別リソース層
modulemodule <name> '<path>' = { ... }複数リソース束ね層
typetype <name> = <type-expression>ユーザ定義型(union, object)
funcfunc <name> (...) <type> => <expr>ユーザ定義関数
var / paramvar <name> = <expr> / param <name> <type> = <default>ローカル変数 / デプロイ時パラメータ

3自由度:

2×2配置:外部DSL × 言語が決める。3自由度すべてが言語側で制限される典型例。

設計判断: ARM template (JSON)の手書きを置き換えるauthoring layer。コンパイル先がJSONなのでARM templateの表現力が天井になる(「実行時に別templateを読み込む」はARM templateの世界に存在しないのでBicepでも実装できない)。制約表現は @minLength, @maxLength, @allowed 等のdecorator中心。得たものはARM templateへの1:1対応の予測性、エディタ/lint/IntelliSenseの効きやすさ、Azure専用に特化した型システム。失ったものは任意のヘルパー抽象化、cross-cloudの汎用性、動的なmodule選択。

KCL (CNCF)

公式: https://www.kcl-lang.io/docs/reference/lang/tour

記述言語の位置づけ:外部DSL。Kubernetes / Cloud Native寄りの構成記述言語でPythonに似た見た目、CNCF Sandboxプロジェクト。評価モデルは純粋関数("KCL functions cannot modify external variables, but can only reference external variables.")、immutability優先、schemaは順序非依存(計算は依存ベース)。

抽象化機構:

単位構文役割
moduleファイル / ディレクトリ名前空間、importの単位
schemaschema Foo: ...型 + 制約 + デフォルト値の定義
lambdalambda x: int, y: int -> int { x + y }関数値(一級)
ruleランタイム制約検証バリデーション
mixin / protocolschema合成 / 制約多重継承的な再利用

schemaは単一継承 + mixin合成(schema Employee(Person): / mixin [FullNameMixin])。

3自由度:

2×2配置:外部DSL × ハイブリッド(3自由度のうち2つが「言語が決める」、1つが「規約に委ねる」)。Pklとの違いは、Pklがループ構文なしで標準ライブラリ map/filter のみなのに対し、KCLはlist/dict comprehensionを持つ点。

設計判断: 「Pythonの見た目を借りつつ、副作用と動的読み込みを禁止する」。得たものはPython経験者の学習コストの低さ、構成データ計算の表現力、schemaの型安全と制約検証。失ったものは副作用ベースの抽象化と動的module選択。

CUE

公式: https://cuelang.org/docs/reference/spec/

記述言語の位置づけ:外部DSL。data validation / configuration / code generationの特化言語で、Goから派生したが言語仕様は独立。評価モデルはunification中心(a & b はgreatest lower boundを計算する二項演算子で、結合則・交換則・冪等性を満たす)、順序非依存("combining CUE values in any order always gives the same result")、constraint = type = valueをgraph unification modelで単一概念として扱う。

抽象化機構:

単位構文役割
packageディレクトリ単位名前空間
definition#Foo: { ... }型(closed struct)
regular fieldfoo: ...
comprehension[for x in a if cond { x+1 }]集合計算

「型と値を区別しない」設計で、#Foo: { name: string } は型として、foo: { name: "alice" } は値として、両者を & でunifyできる。

3自由度:

2×2配置:外部DSL × 言語が決める(3自由度すべて)。Terraform / Carina / Bicepと同じ象限だが、抽象化機構がunificationという独自の数学的基礎に乗っている点で特殊。

設計判断: 「unificationに賭けた純粋関数型DSL」。「抽象化 = 関数」ではなく「抽象化 = constraintのunification」で、関数で値を計算する代わりにdefinitionで制約を書きunifyで組み合わせる。順序非依存 / 結合則 / 交換則 / 冪等により部分評価・統合・検証が可能になる。Turing不完全(仕様レベルで停止性を保つために関数定義や再帰を許さない)。得たものは構成データの数学的取り扱い、停止性保証、validation/policyとしての強さ。失ったものは命令的な計算ロジックの抽象化と命令型プログラマの直感との整合。

Dhall

公式: https://dhall-lang.org/ / https://github.com/dhall-lang/dhall-lang

記述言語の位置づけ:外部DSL。"JSON + functions + types + imports" を標榜する純粋関数型 + 全域(total)な構成記述言語。Turing不完全("Dhall is not Turing-complete. Evaluation always terminates, no exceptions")、全域関数型(System F-ω + α拡張、Haskellに近い表現力で再帰は禁止、代わりに Natural/fold 等の組込み再帰子のみ)、純粋(評価結果はnormal formにreduceされる)、強い静的型付け。

抽象化機構:

単位構文役割
lambdaλ(x: T) → expr関数値(一級)
letlet x = ... in ...局所束縛
record{ name = "alice", age = 30 }値の集約
union< Some : Text | None >直和型
import./foo.dhall / https://...別module読み込み

3自由度:

2×2配置:外部DSL × ハイブリッド(Pkl / KCLと類似象限)。

設計判断: 「Turing不完全 + 純粋関数型 + 静的import + 暗号学的hash」。importのhash integrity (let pkg = https://example.com/pkg.dhall sha256:abc...)で改竄検出が可能。normal formにより、どんなに抽象化を重ねても最終的には関数もimportも解決した形に到達する。得たものは停止性、再現性、構成データの数学的厳格さ。失ったものは任意再帰の表現力、ストリーミング処理、部分的なlazy評価。CUEと並ぶ純粋実装だが、CUEがunification中心なのに対しDhallはlambda calculus + importというHaskell的な系譜。

Jsonnet

公式: https://jsonnet.org/

記述言語の位置づけ:外部DSL (JSON拡張)。Google由来のdata templating languageで、"Any JSON document is a valid Jsonnet program" — JSONの上位互換。評価モデルはlazy("variable initializers are not evaluated until the variable is used")、純粋関数型、ただし error 文で停止可能なため部分的にnon-total。

抽象化機構:

単位構文役割
functionlocal my_function(x, y=10) = x + y;関数定義
lambda(function(x) x * x)(5)関数値
object{ name: "alice", greet():: "hi" }レコード
mixin / +Base + { f: 5 }, Base { f: 5 }合成
importlocal lib = import 'lib.libsonnet';別ファイル読み込み

オブジェクト合成はmixin型で、super で基底参照、self 後期束縛、:: でhidden field、+: でdeep merge。

3自由度:

2×2配置:外部DSL × ハイブリッド(Pkl / KCL / Dhallと同じパターン)。

設計判断: 「JSON互換 + lazy + 純粋関数型 + mixin合成」。既存JSONツールチェーンとの相互運用が容易で学習コストが低い。mixin合成はクラス継承ではなく + 演算子による合成(CUEのunificationに似るが + は非可換で右辺が勝つ)。得たものは既存JSON設定の拡張パスとしての自然さ、lazy評価による柔軟な参照解決、シンプルなmixin。失ったものは静的型(型システムなし、CUE / Pkl / KCLよりruntime errorが多い)と停止性保証。

Nickel (Tweag)

公式: https://nickel-lang.org/

記述言語の位置づけ:外部DSL。Tweagが開発した構成記述向けの汎用関数型言語で、Nix / Dhall / CUEの系譜。lazy(Nix的)、純粋関数型、opt-in静的型 + ランタイムcontracts(型は付けても付けなくてもよい。contractsは値検証)、CUE的なmerge演算子 &

抽象化機構:

単位構文役割
functionfun a b => a + b (curried)関数値(一級)
letlet add1 = add 1 in add1 2局所束縛、部分適用
record{a = 1, b.c = 2}レコード値
enum / matchmatch { 'Some x => x, 'None => 0 }直和型
type / contract5 : Number (型) / 5 | Number (contract)静的検査 / 動的検証
importimport "file.ncl"別ファイル読み込み
merge &{foo = 1} & {bar = 2}レコード合成

mergeにはpriority annotation (default, force, priority N)があり、衝突解消ルールを明示できる。

3自由度:

2×2配置:外部DSL × ハイブリッド。ただし複数量制御も「規約に委ねる」寄り(専用comprehension構文を持たず関数で扱う)で、5DSLの中ではより規約寄りの傾きを持つ。

設計判断: 「lazy + opt-in型 + contracts + merge」。opt-in型は必要な箇所に付けるgradual typing的設計で、contractsは静的型では表現しづらい制約(range、正規表現マッチ等)をruntime validationで表現する。mergeはCUEのunificationに近いが、衝突時の解決ルールを明示する。得たものは段階的に型を導入できる柔軟性、contractsによる動的検証の組み合わせ、mergeによる分散構成の合成。失ったものは静的型強制と停止性保証(Turing完全と思われる)。

cdktf (CDK for Terraform)

公式: https://developer.hashicorp.com/terraform/cdktf

記述言語の位置づけ:汎用言語。TypeScript / Python / Java / C# / Go (CDKと同じくjsiiベース)。コードを実行するとTerraform JSONをsynthする。CDKとの違いは、synth先(CFN template vs Terraform JSON)、対象provider(AWS専用vs Terraform Registry全部)、Constructライブラリの厚み(AWS L2/L3のような厚い抽象は提供されない)。

抽象化機構:

単位構文役割
Appnew App()複数Stackの最上位
TerraformStackclass MyStack extends TerraformStackデプロイ単位(state単位)
Constructclass MyComponent extends Construct再利用単位
Resourcenew aws.s3.S3Bucket(this, 'b', {...})個別リソース
TerraformModulenew TerraformHclModule(this, ...)既存Terraform moduleの埋め込み

constructsライブラリはCDKと共有。

3自由度:

2×2配置:汎用言語(ベタ書き) × 規約に委ねる。CDKと同じ象限。ただし「複数量の制御」で2経路を選べる点が特徴。

設計判断: 「CDKと同じ汎用言語ベース、synth先だけTerraform」。得たものはTerraformエコシステム(provider / state backend / 既存module)を享受しつつ汎用言語で書けること、CDK経験者の移行のしやすさ。失ったものはCDKのAWS L2/L3のような厚い抽象、およびTerraform JSONという中間表現の存在によるdebugの二重化(synth結果とplan結果を両方見る必要)。現実のツールは複数の制御層を併存させるケースがあることを示す例。

cdk8s

公式: https://cdk8s.io/

記述言語の位置づけ:汎用言語。TypeScript / Python / Java / Go (jsiiベース)。コードを実行するとKubernetes YAML manifestsをsynthする。stateなし(synth結果のYAMLを kubectl apply するだけで、stateはKubernetes API serverが持つ)。

抽象化機構:

単位構文役割
Appnew App()複数Chartの最上位
Chartclass MyChart extends ChartYAML manifestの出力単位
Constructclass MyComponent extends Construct再利用単位
ApiObjectnew k.KubeDeployment(this, ...)個別リソース

cdk8s-plusライブラリがDeployment / Service / ConfigMap / Pod / RBAC / Ingress等の高レベル抽象(CDKのL2相当)を提供する。

3自由度:抽象化の追加・複数量の制御・実装の切替すべて規約に委ねる。Kubernetes側にTerraform for_each のような展開機構はない(Helmの range は別レイヤ)ので、cdktfのような「2経路」もなく、ホスト言語1経路で全部扱う。

2×2配置:汎用言語(ベタ書き) × 規約に委ねる。この象限の純粋形。

設計判断: 「CDKと同じ設計をKubernetesに転用」。state管理を持たずKubernetes API serverに委ねる。既存K8s manifestとの互換性があり、Helm chartや生YAMLから段階的に移行できる。得たものは汎用言語の抽象化機構、type safety、IDE統合。失ったものはHelmのようなtemplate配布エコシステム(cdk8s chartはnpm/PyPI経由)とKubernetesコミュニティ主流との距離。

Crossplane Compositions

公式: https://docs.crossplane.io/v2.2/composition/compositions/

記述言語の位置づけ: 外部DSL (YAML) + functionsレイヤ(Go / KCL / Python等) の二層構造。CompositionそのものはKubernetes CRD (apiVersion: apiextensions.crossplane.io/v1, kind: Composition)としてYAMLで定義されるが、Pipeline modeのfunctionsが呼べて、そのfunctionsは任意の言語で実装可能(function-patch-and-transform / function-kcl / function-python等)。

抽象化機構:

単位構文役割
CompositeResourceDefinition (XRD)YAML CRDユーザ向けAPIの型定義
Composite Resource (XR)YAMLXRDを実体化したインスタンス
CompositionYAMLXRから実リソース群を生成するrecipe
Functionコード(Go/KCL/Python/...)パイプラインのステップ
Composed ResourceYAML (関数出力)実際に作られるリソース

Pipeline mode:

spec:
  mode: Pipeline
  pipeline:
  - step: patch-and-transform
    functionRef:
      name: function-patch-and-transform
    input: ...
  - step: kcl-templating
    functionRef:
      name: function-kcl
    input: ...

各functionは前段の出力(composed resourcesの集合)を受け取り、変換して次段に渡す。

3自由度:

2×2配置:外部DSL (YAML) × 多層で、単純2×2に収まらない。最も近いのは「外部DSL × 言語が決める(YAML層) + 任意言語 × 規約に委ねる(function層)」のハイブリッド。

設計判断: 「Kubernetes API上に構築された二層IaC」。YAML層は宣言的に閉じている(kubectlで操作、kustomizeやHelmと組み合わせ可能)一方、function層に拡張性を全部押し付ける(functionはOCIイメージとして配布、複数言語で書ける)。Composition SelectorはXRのlabelに応じて適用Compositionを動的に選択できるため、環境別(dev/staging/prod)の使い分けが可能になる — これはTerraform / Bicep / Carinaにはない動的選択軸。得たものはKubernetes API上の宣言性、functionによる任意言語拡張、Composition Selectorによる環境切替。失ったものは全体像の見通し(YAML + functionコード + XRDの三者を読む必要)、デバッグの複雑化、type safetyの弱さ。「拡張ポイントを言語境界で開ける」という第三の選択肢を示す例で、PulumiのMLCも類似の発想。

Helm

公式: https://helm.sh/docs/chart_template_guide/

記述言語の位置づけ:外部DSL (Go templateにSprig関数ライブラリを足したもの)。YAML manifestに {{ ... }} プレースホルダを埋め込むtemplateベースで、コンパイル先はKubernetes manifest。chart利用者は values.yaml でtemplateを埋める。

抽象化機構:

単位構文役割
Chartディレクトリ単位配布単位
templatetemplates/*.yamlmanifestテンプレート
helper_helpers.tpldefine / template再利用可能なtemplate片
subchartcharts/ ディレクトリ + Chart.yaml のdependencies子chart
values.yamlYAMLパラメタ注入

3自由度:

2×2配置:外部DSL × 言語が決める。Terraform / Carina / Bicep / CUEと同じ象限。

設計判断: 「Go template + YAML mix-in」。values.yamlの分離はchart開発者と利用者の責任分界(chartは機能、valuesは設定)を作る。subchartの静的依存は、Helm chartがartifactとして配布されるため依存がparse時に決定的でないと不便だという要請から来る。得たものはKubernetes設定のwidely-adopted標準、エコシステム(Helm Hub, Artifact Hub)の充実、values.yamlによる設定/機能の分離。失ったものは静的型(template内の式は型なし)、関数の自由度、デバッグの難しさ。typedでない点で同象限の他ツールと異なる。template engine系DSL (Go template, Jinja2等)は「文字列置換 + 限定的な制御フロー」を共通骨格とし、抽象化機構が薄い。

#参照 #dsl #調査