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