Decodal/doc/manual/souce/design/composition-and-materialization.md

3.2 KiB

Composition and Materialization

&//default、materialize は、runtime value の variant に基づいて処理する。

&

& は制約を保った合成である。

compose_and(a: RuntimeValue, b: RuntimeValue) -> RuntimeValue | Diagnostic

基本規則:

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 ごとに再帰合成する。

MyConfig = {
    host = String;
    port = Int default 8080;
};

Config = MyConfig & {
    host = "localhost";
};

hostAbstract(String) & Concrete("localhost") として検証され、成功すれば Concrete("localhost") になる。 port は右辺に明示値がないため、Abstract(Int, default 8080) のまま残る。

default の合成

初期方針では、& による異なる default 同士の合成は conflict とする。 同じ default は同一候補として扱ってよい。

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 である。

patch(a: RuntimeValue, b: RuntimeValue) -> RuntimeValue

基本規則:

  • object / object は field ごとに再帰 patch する。
  • object / object 以外は右辺で置き換える。
  • 左辺にしかない field は保持する。
  • 右辺にしかない field は追加する。
  • 配列、scalar、function は右辺置換とする。

// は制約を保持するための演算子ではない。 制約を満たす具体化には & を使う。

materialize

materialize は runtime value を出力可能な Data に変換する。

materialize(RuntimeValue) -> Data | Diagnostic

処理規則:

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 は採用されない。