120 lines
3.2 KiB
Markdown
120 lines
3.2 KiB
Markdown
# Composition and Materialization
|
|
|
|
`&`、`//`、`default`、materialize は、runtime value の variant に基づいて処理する。
|
|
|
|
## `&`
|
|
|
|
`&` は制約を保った合成である。
|
|
|
|
```text
|
|
compose_and(a: RuntimeValue, b: RuntimeValue) -> RuntimeValue | Diagnostic
|
|
```
|
|
|
|
基本規則:
|
|
|
|
```text
|
|
Abstract(a) & Abstract(b)
|
|
-> Abstract {
|
|
constraints: a.constraints + b.constraints,
|
|
default: merge_default(a.default, b.default)
|
|
}
|
|
|
|
Abstract(a) & Concrete(v)
|
|
-> if satisfies(v, a.constraints) then Concrete(v)
|
|
else constraint diagnostic
|
|
|
|
Concrete(v) & Abstract(a)
|
|
-> if satisfies(v, a.constraints) then Concrete(v)
|
|
else constraint diagnostic
|
|
|
|
Concrete(Object(a)) & Concrete(Object(b))
|
|
-> Concrete(Object(fieldwise_and(a, b)))
|
|
|
|
Concrete(a) & Concrete(b)
|
|
-> if a == b then Concrete(a)
|
|
else conflict diagnostic
|
|
```
|
|
|
|
`Abstract & Concrete` が成功した場合、default は消える。
|
|
明示値があるなら fallback は不要だからである。
|
|
|
|
## object の合成
|
|
|
|
object は concrete structure だが、field の値は thunk 経由で concrete / abstract のどちらにもなりうる。
|
|
object 同士の `&` は field ごとに再帰合成する。
|
|
|
|
```dcdl
|
|
MyConfig = {
|
|
host = String;
|
|
port = Int default 8080;
|
|
};
|
|
|
|
Config = MyConfig & {
|
|
host = "localhost";
|
|
};
|
|
```
|
|
|
|
`host` は `Abstract(String) & Concrete("localhost")` として検証され、成功すれば `Concrete("localhost")` になる。
|
|
`port` は右辺に明示値がないため、`Abstract(Int, default 8080)` のまま残る。
|
|
|
|
## default の合成
|
|
|
|
初期方針では、`&` による異なる default 同士の合成は conflict とする。
|
|
同じ default は同一候補として扱ってよい。
|
|
|
|
```text
|
|
merge_default(None, None) -> None
|
|
merge_default(Some(a), None) -> Some(a)
|
|
merge_default(None, Some(b)) -> Some(b)
|
|
merge_default(Some(a), Some(b)) -> if same(a, b) then Some(a) else conflict
|
|
```
|
|
|
|
`//` では右辺 default が左辺 default を置き換える。
|
|
|
|
## `//`
|
|
|
|
`//` は右辺優先の deep patch である。
|
|
|
|
```text
|
|
patch(a: RuntimeValue, b: RuntimeValue) -> RuntimeValue
|
|
```
|
|
|
|
基本規則:
|
|
|
|
- object / object は field ごとに再帰 patch する。
|
|
- object / object 以外は右辺で置き換える。
|
|
- 左辺にしかない field は保持する。
|
|
- 右辺にしかない field は追加する。
|
|
- 配列、scalar、function は右辺置換とする。
|
|
|
|
`//` は制約を保持するための演算子ではない。
|
|
制約を満たす具体化には `&` を使う。
|
|
|
|
## materialize
|
|
|
|
materialize は runtime value を出力可能な `Data` に変換する。
|
|
|
|
```text
|
|
materialize(RuntimeValue) -> Data | Diagnostic
|
|
```
|
|
|
|
処理規則:
|
|
|
|
```text
|
|
Concrete(String/Int/Float/Bool) -> Data
|
|
Concrete(Array(items)) -> each item を force して materialize
|
|
Concrete(Object(fields)) -> each field を force して materialize
|
|
Concrete(Function) -> materialize 不能
|
|
|
|
Abstract { constraints, default: Some(d) }
|
|
-> force(d)
|
|
-> result が constraints を満たすか検証
|
|
-> materialize(result)
|
|
|
|
Abstract { constraints, default: None }
|
|
-> 未解決 abstract value として diagnostic
|
|
```
|
|
|
|
materialize は default を採用する唯一の段階である。
|
|
通常評価中に明示値が得られた場合、default は採用されない。
|