Add associative map constraints and range refinement

This commit is contained in:
2026-08-14 06:26:15 +09:00
parent 8198b615a8
commit cc6ab40807
52 changed files with 8951 additions and 6918 deletions
@@ -1,6 +1,6 @@
# Composition and Materialization
`&``//``default`、materialize は、runtime value の variant に基づいて処理する。
`&``//``as``default`、materialize は、runtime value の variant に基づいて処理する。
## `&`
@@ -89,7 +89,24 @@ patch(a: RuntimeValue, b: RuntimeValue) -> RuntimeValue
- 配列、scalar、function は右辺置換とする。
`//` は制約を保持するための演算子ではない。
制約を満たす具体化に`&` を使う。
対称な制約合成には `&` を使い、左辺が右辺より狭いことを確認しながら合成する場合`as` を使う。
## `as`
`as` は narrower と wider の向きを固定した範囲包含確認と合成である。
```text
apply_as(narrower: RuntimeValue, wider: RuntimeValue)
-> RuntimeValue | Diagnostic
```
abstract / abstract では、左辺 constraints が右辺 constraints を包含することを確認し、左辺を結果にする。
concrete / abstract では、concrete value が右辺 constraints を満たすことを確認する。
object / object では右辺を field domain とする。左辺にしかない field は診断し、両側にある field は再帰的に `apply_as` し、右辺にしかない field は thunk を force せずそのまま結果へ残す。
この扱いは default の有無に依存しない。default は包含判定の根拠でも `as` の補完値でもなく、後の materialize だけが選択する。
`ArrayItems(wider)` は concrete array の全要素を、`MapValues(wider)` は concrete object の全 field value を同じ関数で絞り込む。
## materialize
+6 -2
View File
@@ -41,10 +41,10 @@ engine.bind_global(
A user source can then refer to `Service` without importing it.
```dcdl
Service & {
{
name = "api";
port = 9443;
}
} as Service
```
## HostValue
@@ -60,6 +60,7 @@ HostValue =
Bool
Array(Vec<HostValue>)
ArrayConstraint { item, constraints, default }
MapConstraint { value, constraints, default }
Object(Vec<HostField>)
Abstract { constraints, default }
```
@@ -69,6 +70,9 @@ When a host value is bound, the engine internalizes it into `RuntimeValue` and a
`HostValue::array_of(item)` builds an array constraint with a required element schema.
There is no host API for an unconstrained abstract array.
`HostValue::map_of(value)` builds a map constraint whose arbitrary object field values must satisfy `value`.
`BTreeMap<String, T>` and, with `std`, `HashMap<String, T>` implement `DecodalSchema`, `DecodalDecode`, and `IntoHostValue` using this representation.
## Abstract host objects
A host-provided schema object is represented as a concrete object structure whose fields may contain abstract values.
+11 -3
View File
@@ -16,7 +16,8 @@ demand-driven evaluation
├─ load imported module on demand
├─ evaluate expression
├─ compose `&`
─ patch `//`
─ patch `//`
└─ validate `as`
materialize
@@ -105,9 +106,16 @@ eval(A // B):
a = eval(A)
b = eval(B)
patch(a, b)
eval(A as S):
narrower = eval(A)
wider = eval(S)
apply_as(narrower, wider)
```
`&` は制約を保った合成を行い、`//` は右辺優先の deep patch を行う。
`&` は制約を保った対称な合成を行い、`//` は右辺優先の deep patch を行う。
`as` は左辺が右辺より狭いことを再帰的に確認する。左辺で絞られた部分は左辺を使い、右辺だけの部分は abstract のまま保持する。
default は `as` では選択せず、materialize まで遅延する。
詳細は [Composition and Materialization](./composition-and-materialization.md) に置く。
## resolver / binder
@@ -140,7 +148,7 @@ resolver / binder を追加すると、以下を早期に診断しやすくな
- import path の一部静的解決
- span 付き diagnostic の精度向上
ただし、Decodal の制約検証は独立した type checking pass ではなく、`&` の合成時materialize 時に行う。
ただし、Decodal の制約検証は独立した type checking pass ではなく、`&` の合成時`as` の適用時、materialize 時に行う。
## materialize
+1
View File
@@ -10,6 +10,7 @@ Core に入れる機能は、基本的に deterministic な value transformation
- arithmetic / logical / comparison operators
- array concat
- object / constraint composition
- asymmetric range refinement and homogeneous map constraints
- default materialization
- pure function evaluation
- host supplied import evaluation
+1 -1
View File
@@ -6,7 +6,7 @@
## 方針
初期処理系は AST interpreter として実装する。
bytecode VM や JIT ではなく、AST を demand-driven に評価することで、遅延評価、循環参照、`default``&``//` の意味論を小さく実装する。
bytecode VM や JIT ではなく、AST を demand-driven に評価することで、遅延評価、循環参照、`default``&``//``as` の意味論を小さく実装する。
## 構成
+4
View File
@@ -124,6 +124,7 @@ constraint は concrete value とは別の型として扱う。
Constraint =
Type(PrimitiveType)
ArrayItems(ThunkId)
MapValues(ThunkId)
Compare(Op, Literal)
Regex(Pattern)
BuiltinPredicate(Symbol)
@@ -133,6 +134,9 @@ Constraint =
`ArrayItems` は配列そのものの型と、すべての要素へ合成する schema thunk を表す。
配列用の primitive type は持たず、抽象配列には必ず要素制約が必要である。
`MapValues` は object の key 集合を制限せず、すべての field value へ適用する schema thunk を表す。
連想配列は materialize 後も `Data::Object` になり、別の data variant は持たない。
初期実装では、object の形は主に `Concrete(Object)` の field に `Abstract` を置くことで表現する。
object 全体にかかる constraint は必要になった時点で追加する。