# 合成演算子 この章では、`&` と `//` の意味を定義する。 ## `&`: 制約合成 `&` は値・制約・構造を合成する演算子である。 ```dcdl A & B ``` 基本規則: - 両方が制約なら、両方を満たす制約になる。 - 制約と具体値なら、具体値が制約を満たす必要がある。 - 両方が同じ具体値なら、その値になる。 - 両方が異なる具体値なら conflict になる。 - 両方が object なら、フィールドごとに合成する。 - 同じフィールドが両方にある場合、そのフィールド値を `&` で合成する。 - 矛盾が発生した場合はエラーになる。 例: ```dcdl Port = Int & >= 1 & <= 65535; NarrowedPort = Port & > 443; MyConfig = { port = NarrowedPort default 8080; }; Config = MyConfig & { port = 8000; }; ``` `Config.port` は概念的には以下になる。 ```text NarrowedPort & 8000 ``` `8000` は `NarrowedPort` を満たすため成功する。 一方、以下は失敗する。 ```dcdl BadConfig = MyConfig & { port = 80; }; ``` `80` は `> 443` を満たさないためである。 ## `//`: patch 合成 `//` は右辺優先の構造的 patch 演算子である。 `&` が制約を保った合成であるのに対し、`//` は設定やスキーマを上書き・変更するために使う。 ```dcdl A // B ``` 基本規則: - 両方が object なら、フィールドごとに再帰的に patch する。 - 同じフィールドが object/object なら、さらに再帰的に patch する。 - 同じフィールドが object/object 以外なら、右辺で置き換える。 - 左辺にしかないフィールドは保持する。 - 右辺にしかないフィールドは追加する。 - 配列はデフォルトでは右辺で置き換える。 - 関数値はデフォルトでは右辺で置き換える。 つまり `//` は shallow merge ではなく deep patch とする。 ```dcdl Base = { feature_hoge = { enable = Bool default true; fuga = Int default 10; }; }; Patched = Base // { feature_hoge = { enable = false; }; }; ``` `Patched` は以下に相当する。 ```dcdl { feature_hoge = { enable = false; fuga = Int default 10; }; } ``` ドットパスを使うと以下のようにも書ける。 ```dcdl Patched = Base // { feature_hoge.enable = false; }; ``` ## object 全体の置換 `//` が deep patch である場合、object 全体を置き換えたいときの escape hatch が必要になる。 候補として、`replace(...)` を組み込み関数として提供する。 ```dcdl Replaced = Base // { feature_hoge = replace({ enable = false; }); }; ``` この場合、`feature_hoge` は再帰 patch されず、右辺の object に丸ごと置き換わる。 ## `&` と `//` の使い分け `&` は制約を満たす具体化に使う。 ```dcdl ValidConfig = MyConfig & { port = 8000; }; ``` `//` は既存構造の上書きや変形に使う。 ```dcdl ModifiedSchema = MyConfig // { port = Int default 9000; }; ``` `//` は右辺優先の patch であり、左辺の制約を常に保持するとは限らない。 制約を保持したい場合は `&` を使う。