# Runtime Model 処理系の評価結果は、具体値と抽象値を区別した runtime value として扱う。 `value`、`constraints`、`default` を横並びに持つ構造にはしない。 ## RuntimeValue ```text RuntimeValue = Concrete(ConcreteValue) Abstract(AbstractValue) ``` `Concrete` は明示的な値である。 `Abstract` は、まだ具体値に確定していない制約付きの値である。 Decodal は `Unknown` を、すべてのDecodal値を包含する最上位の abstract range として持つ。 これは存在する具体値の型情報を消す `Any` ではない。具体値を `Unknown` に対して検証した場合は具体値を保持し、`Unknown` のままmaterializeしようとした場合は diagnostic になる。 識別子や field が解決できない状態とは区別し、それらは従来どおりその場で diagnostic になる。 ## ConcreteValue ```text ConcreteValue = String(String) Int(i64) Float(f64) Bool(bool) Array(Vec) Object(ObjectValue) Function(FunctionValue) ``` object は concrete structure として扱う。 ただし、各 field の中身は concrete value でも abstract value でもよい。 ```text ObjectValue: fields: Map rest: Option ObjectField: value: ThunkId span: Span ObjectRest: value: ThunkId span: Span ``` 例えば以下の schema object は、object 自体は concrete だが、field の値は abstract value になる。 ```dcdl MyConfig = { host = String; port = Int default 8080; }; ``` 概念的には以下である。 ```text Concrete(Object { host -> Thunk(Abstract { constraints: [String], default: none }) port -> Thunk(Abstract { constraints: [Int], default: 8080 }) }) ``` ## AbstractValue ```text AbstractValue { constraints: Vec default: Option } ConstraintEntry { constraint: Constraint span: Span } ``` `default` は `AbstractValue` にだけ存在する。 明示的な concrete value がある場合、default は保持しない。 ```dcdl port = Int default 8080; ``` これは以下の runtime value になる。 ```text Abstract { constraints: [Type(Int)] default: Some(Thunk(8080)) } ``` ```dcdl port = 8000; ``` これは以下である。 ```text Concrete(Int(8000)) ``` ## Runtime scope Decodal runtime は application runtime ではなく、pure value evaluator である。 同じ source、同じ import results、同じ host globals が与えられた場合、評価結果は決定的である。 runtime が扱う責務は以下に限る。 - expression を評価する。 - thunk を必要に応じて force する。 - concrete / abstract value を合成する。 - materialize 時に constraint を検証する。 runtime は filesystem、network、environment variable、time、random、mutation を扱わない。 core における import は host supplied source または structured value を受け取る境界であり、filesystem access ではない。 ## Constraint constraint は concrete value とは別の型として扱う。 ```text Constraint = Unknown Type(PrimitiveType) ArrayItems(ThunkId) MapValues(ThunkId) Compare(Op, Literal) Regex(Pattern) BuiltinPredicate(Symbol) ObjectConstraint(...) ``` `ArrayItems` は配列そのものの型と、すべての要素へ合成する schema thunk を表す。 配列用の primitive type は持たず、抽象配列には必ず要素制約が必要である。 `MapValues` は object の key 集合を制限せず、すべての field value へ適用する schema thunk を表す。 連想配列は materialize 後も `Data::Object` になり、別の data variant は持たない。 `ObjectValue.rest` は名前付きfieldを持つobjectの残余field rangeを表す。rest自体はfieldを生成せず、materializeは実在するfieldだけを出力する。 初期実装では、object の形は主に `Concrete(Object)` の field に `Abstract` を置くことで表現する。 object 全体にかかる constraint は必要になった時点で追加する。 ## Data materialize 後の出力可能な値は runtime value とは別型にする。 ```text Data = String(String) Int(i64) Float(f64) Bool(bool) Array(Vec) Object(Map) ``` `Function`、未解決の `Abstract`、未評価の thunk は `Data` にはならない。 ## 命名 実装内部では `RuntimeValue` を短く `Value` と呼んでもよい。 ただし、materialize 後の出力値とは区別する。 推奨する区別: ```text RuntimeValue / Value 言語内部の評価結果。Abstract を含む。 ConcreteValue 明示的な具体値。 AbstractValue constraint と default を持つ抽象値。 Data 外部へ出力可能な最終データ。 ```