From 6d80c661442072dc84de6647754521cc1477e5d3 Mon Sep 17 00:00:00 2001 From: Hare Date: Thu, 9 Jul 2026 02:46:28 +0900 Subject: [PATCH] Trim open issues to materialization --- .../design/composition-and-materialization.md | 5 +- .../souce/design/thunk-and-lazy-evaluation.md | 2 +- .../language/constraints-and-defaults.md | 13 ++-- doc/manual/souce/language/evaluation.md | 2 +- doc/manual/souce/language/examples.md | 40 ++++------- .../language/expression/function-call.md | 8 +-- .../souce/language/expression/function.md | 6 +- doc/manual/souce/language/expression/let.md | 8 +-- .../language/expression/path-reference.md | 3 +- .../expression/string-interpolation.md | 14 ++-- doc/manual/souce/language/functions.md | 71 +++++-------------- .../language/materialization-and-errors.md | 3 +- .../souce/language/modules-and-imports.md | 3 +- doc/manual/souce/language/operators.md | 15 +--- doc/manual/souce/language/syntax.md | 9 +-- doc/manual/souce/language/value/float.md | 9 ++- doc/manual/souce/language/value/string.md | 3 +- doc/manual/souce/open-issues.md | 65 ++++------------- 18 files changed, 92 insertions(+), 187 deletions(-) diff --git a/doc/manual/souce/design/composition-and-materialization.md b/doc/manual/souce/design/composition-and-materialization.md index 6792737..3e6aec5 100644 --- a/doc/manual/souce/design/composition-and-materialization.md +++ b/doc/manual/souce/design/composition-and-materialization.md @@ -59,8 +59,9 @@ Config = MyConfig & { ## default の合成 -初期方針では、`&` による異なる default 同士の合成は conflict とする。 -同じ default は同一候補として扱ってよい。 +`&` で片方だけが default を持つ場合、その default を保持する。 +同じ thunk 由来の default 同士は同一 default として保持する。 +異なる default 同士は conflict とする。 ```text merge_default(None, None) -> None diff --git a/doc/manual/souce/design/thunk-and-lazy-evaluation.md b/doc/manual/souce/design/thunk-and-lazy-evaluation.md index 9153247..5166869 100644 --- a/doc/manual/souce/design/thunk-and-lazy-evaluation.md +++ b/doc/manual/souce/design/thunk-and-lazy-evaluation.md @@ -84,5 +84,5 @@ module root や field は thunk として保持され、参照されたときだ 関数引数は thunk として関数の environment に束縛する。 関数本体で引数が参照されたときだけ force する。 -任意の関数呼び出し結果をグローバルに memoize する必要はない。 +関数呼び出し結果そのものはグローバルには memoize しない。 field に束縛された関数呼び出し結果は、その field thunk の評価結果として memoize される。 diff --git a/doc/manual/souce/language/constraints-and-defaults.md b/doc/manual/souce/language/constraints-and-defaults.md index 5e8b384..1150b47 100644 --- a/doc/manual/souce/language/constraints-and-defaults.md +++ b/doc/manual/souce/language/constraints-and-defaults.md @@ -188,11 +188,12 @@ RuntimeValue = ## default の合成 -同じフィールドに複数の `default` が合成された場合の詳細規則は未確定である。 -現時点の単純な方針は以下である。 +`default` の合成規則は以下である。 -- `&` による default 同士の衝突はエラーにする。 -- 同一 default 値は許可してよい。 -- `//` による patch では右辺 default が左辺 default を置き換える。 +- `&` で片方だけが default を持つ場合、その default を保持する。 +- `&` で両方が異なる default を持つ場合、conflict になる。 +- `Abstract & Concrete` が成功した場合、結果は concrete value になり default は消える。 +- `//` では右辺が左辺を置き換える。object/object の場合は field ごとに再帰 patch されるため、右辺 field の default が左辺 field の default を置き換える。 -この方針により、`&` は制約を保った合成、`//` は上書き操作として説明できる。 +`default` thunk は materialize 時に必要になった時点で評価する。 +評価された default value は、同じ abstract value に残っている制約を満たす必要がある。 diff --git a/doc/manual/souce/language/evaluation.md b/doc/manual/souce/language/evaluation.md index 7a29edf..d3b3f73 100644 --- a/doc/manual/souce/language/evaluation.md +++ b/doc/manual/souce/language/evaluation.md @@ -74,5 +74,5 @@ Error 評価失敗 関数引数は thunk として渡せる。 関数本体内で引数が参照されたときに評価する。 -任意の関数呼び出し結果をグローバルに memoize することは必須ではない。 +関数呼び出し結果そのものはグローバルには memoize しない。 ただし、フィールドに束縛された呼び出し結果は、そのフィールド thunk の評価結果として memoize される。 diff --git a/doc/manual/souce/language/examples.md b/doc/manual/souce/language/examples.md index 6916130..52e36d7 100644 --- a/doc/manual/souce/language/examples.md +++ b/doc/manual/souce/language/examples.md @@ -5,21 +5,19 @@ ## 基本的な設定スキーマ ```dcdl -rec { - Host = IPv4Address; +Host = IPv4Address; - Port = Int & >= 1 & <= 65535; - NarrowedPort = Port & > 443; +Port = Int & >= 1 & <= 65535; +NarrowedPort = Port & > 443; - MyConfig = { - host = Host; - port = NarrowedPort default 8080; - feature_hoge = { - enable = Bool default true; - fuga = Int default 10; - }; +MyConfig = { + host = Host; + port = NarrowedPort default 8080; + feature_hoge = { + enable = Bool default true; + fuga = Int default 10; }; -} +}; NewConfig = MyConfig & { host = "127.0.0.1"; @@ -33,28 +31,20 @@ disabled_config = NewConfig & { enabled_config = NewConfig; ``` -## 関数と文字列生成 +## 関数と制約 ```dcdl let - maybe_hw = String & /Hello! .*/; - part = { - greet = String; - target = String; - }; - mk_hw = (part: part) => - maybe_hw & "${part.greet}! ${part.target}"; + Port = Int & >= 1 & <= 65535; + add_offset = (base: Port, offset: Int) => base + offset; in - mk_hw({ - greet = "Hello"; - target = "World"; - }) + add_offset(8000, 80) ``` 評価結果: ```text -"Hello! World" +8080 ``` ## match diff --git a/doc/manual/souce/language/expression/function-call.md b/doc/manual/souce/language/expression/function-call.md index 7c79995..ca3fcb2 100644 --- a/doc/manual/souce/language/expression/function-call.md +++ b/doc/manual/souce/language/expression/function-call.md @@ -3,10 +3,7 @@ function call expression は、関数値を引数に適用する式である。 ```dcdl -mk_hw({ - greet = "Hello"; - target = "World"; -}) +increment(41) ``` ## 評価 @@ -14,4 +11,5 @@ mk_hw({ 引数は thunk として渡せる。 関数本体内で引数が参照されたときに評価する。 -任意の関数呼び出し結果をグローバルに memoize することは必須ではない。 +関数呼び出し結果そのものはグローバルには memoize しない。 +フィールドに束縛された呼び出し結果は、そのフィールド thunk の評価結果として memoize される。 diff --git a/doc/manual/souce/language/expression/function.md b/doc/manual/souce/language/expression/function.md index 2129aa1..fbefbf7 100644 --- a/doc/manual/souce/language/expression/function.md +++ b/doc/manual/souce/language/expression/function.md @@ -3,11 +3,7 @@ function expression は、引数を受け取り式を返す値である。 ```dcdl -(part: { - greet = String; - target = String; -}) => - "${part.greet}! ${part.target}" +(value: Int) => value + 1 ``` 関数仕様の詳細は [関数](../functions.md) に置く。 diff --git a/doc/manual/souce/language/expression/let.md b/doc/manual/souce/language/expression/let.md index 4ea14fd..dabb956 100644 --- a/doc/manual/souce/language/expression/let.md +++ b/doc/manual/souce/language/expression/let.md @@ -4,12 +4,10 @@ let expression は、ローカル束縛を作る。 ```dcdl let - part = { - greet = "Hello"; - target = "World"; - }; + base = 8000; + offset = 80; in - "${part.greet}! ${part.target}" + base + offset ``` ## 評価 diff --git a/doc/manual/souce/language/expression/path-reference.md b/doc/manual/souce/language/expression/path-reference.md index 221dc27..b9030ab 100644 --- a/doc/manual/souce/language/expression/path-reference.md +++ b/doc/manual/souce/language/expression/path-reference.md @@ -12,4 +12,5 @@ config.feature_hoge.enable 左側の式を object として評価し、指定された field を参照する。 参照先 field は必要になるまで評価されない。 -存在しない field への参照をエラーにするか、open schema として扱うかは未確定である。 +存在しない field への参照は diagnostic になる。 +Decodal は unknown / Any のような値を伝播せず、field の有無を曖昧にしない。 diff --git a/doc/manual/souce/language/expression/string-interpolation.md b/doc/manual/souce/language/expression/string-interpolation.md index b6b656a..3e00090 100644 --- a/doc/manual/souce/language/expression/string-interpolation.md +++ b/doc/manual/souce/language/expression/string-interpolation.md @@ -1,12 +1,8 @@ -# String Interpolation Expression +# String Interpolation -string interpolation は、文字列内に式を埋め込む候補機能である。 +文字列補間は初期実装には含めない。 -```dcdl -"${part.greet}! ${part.target}" -``` +Decodal の string literal は、現時点では literal text として扱う。 +式を埋め込む構文は定義しない。 -## 評価 - -補間式の評価タイミングは通常の遅延評価に従う。 -文字列補間を初期実装に含めるかは未確定である。 +必要になった場合は、文字列連結や明示的な formatting function として別途設計する。 diff --git a/doc/manual/souce/language/functions.md b/doc/manual/souce/language/functions.md index 7ec62dd..93788fb 100644 --- a/doc/manual/souce/language/functions.md +++ b/doc/manual/souce/language/functions.md @@ -5,54 +5,47 @@ ## 構文 ```dcdl -(part: { - greet = String; - target = String; -}) => - "${part.greet}! ${part.target}" +(value: Int) => value + 1 ``` 関数呼び出しは通常の呼び出し構文で行う。 ```dcdl -mk_hw({ - greet = "Hello"; - target = "World"; -}) +let + increment = (value: Int) => value + 1; +in + increment(41) ``` -複数引数の構文は候補として以下を想定する。 +複数引数も指定できる。 ```dcdl -( - input_a: { - hoge = Int & >= 0; - }, - input_b: { - fuga = 20; - } -) => +(input_a: { hoge = Int & >= 0; }, input_b: { fuga = Int; }) => { - # ... + hoge = input_a.hoge; + fuga = input_b.fuga; } ``` ## 関数の意味 -関数は値として扱える。 -ただし、最終データとして関数値を出力できるかどうかは別途定める。 -設定ファイルを materialize する段階では、未適用の関数値は出力不能な値として扱うのが自然である。 +関数は runtime value として扱えるが、最終データとして materialize することはできない。 +未適用の関数値が materialize 対象に残っている場合は diagnostic になる。 + +関数値は opaque であり、関数値同士の等価性は提供しない。 +`&` で関数値同士を合成すると conflict になる。 ## 評価方針 -関数は以下の方針を基本とする。 +関数は以下の方針で評価する。 - 関数は純粋である。 - 関数はレキシカルスコープを持つ。 - 関数は定義時の環境を参照として保持する。 -- 引数は必要になるまで評価しない。 -- フィールドに束縛された関数呼び出し結果は、そのフィールド評価結果として memoize される。 -- 任意の関数呼び出しそのものをグローバルに memoize することは必須ではない。 +- 引数は thunk として渡され、必要になるまで評価されない。 +- 関数呼び出し結果そのものはグローバルには memoize しない。 +- フィールドに束縛された関数呼び出し結果は、そのフィールド thunk の評価結果として memoize される。 +- 再帰的な依存は thunk cycle として diagnostic になる。 関数値の内部モデル例: @@ -63,29 +56,3 @@ Function { env: EnvRef } ``` - -## 関数と重さ - -関数の文法・パーサー自体は大きくない。 -実装上の重さは、主に評価モデルと意味論から発生する。 - -注意点: - -- クロージャが環境を掴む。 -- 引数を lazy にするか strict にするかを決める必要がある。 -- 関数呼び出し結果をどこまで memoize するかを決める必要がある。 -- 再帰関数を許可するかを決める必要がある。 -- 関数値同士の `&` をどう扱うかを決める必要がある。 -- 関数値を object に入れたとき、materialize 可能かを決める必要がある。 -- import 循環と関数適用が絡んだときの cycle detection が必要になる。 - -軽量に保つため、初期仕様では以下の制限を検討できる。 - -- 関数は pure。 -- lexical closure は許可。 -- 引数は thunk として渡す。 -- 関数本体は必要になるまで評価しない。 -- 関数値は opaque。 -- 関数同士の `&` は、同一関数参照以外は conflict。 -- 関数値は最終データとして出力できない。 -- 再帰は cycle error として扱う、または v1 では禁止する。 diff --git a/doc/manual/souce/language/materialization-and-errors.md b/doc/manual/souce/language/materialization-and-errors.md index f8613f6..53d5870 100644 --- a/doc/manual/souce/language/materialization-and-errors.md +++ b/doc/manual/souce/language/materialization-and-errors.md @@ -95,4 +95,5 @@ match value { 通常の式はエラー内容に基づいて分岐できない。 汎用 `try / catch` は core には含めない。 -fallback は `default`、`match`、および将来的な optional import / optional field access のような限定された仕組みで表現する。 +fallback は `default` と `match` で表現する。 +optional import や optional field access は core には含めない。 diff --git a/doc/manual/souce/language/modules-and-imports.md b/doc/manual/souce/language/modules-and-imports.md index 0208996..f28273c 100644 --- a/doc/manual/souce/language/modules-and-imports.md +++ b/doc/manual/souce/language/modules-and-imports.md @@ -29,7 +29,8 @@ result = schema; ``` この場合、`result` の右辺の `schema` は同じ module の top-level field `schema` を参照する。 -通常の object literal 内の field を暗黙に recursive scope にするかは別仕様とする。 +通常の object literal 内の field は、その object 内の sibling field を暗黙には識別子として参照できない。 +object 内の値を参照する場合は、外側で束縛された値や明示的な path reference を使う。 ## SourceLoader diff --git a/doc/manual/souce/language/operators.md b/doc/manual/souce/language/operators.md index c6123cc..5e296f2 100644 --- a/doc/manual/souce/language/operators.md +++ b/doc/manual/souce/language/operators.md @@ -183,19 +183,10 @@ Patched = Base // { ## object 全体の置換 -`//` が deep patch である場合、object 全体を置き換えたいときの escape hatch が必要になる。 +`//` では object/object は常に deep patch される。 +object field 全体を特別に置き換えるための `replace(...)` 構文や組み込み関数は core には含めない。 -候補として、`replace(...)` を組み込み関数として提供する。 - -```dcdl -Replaced = Base // { - feature_hoge = replace({ - enable = false; - }); -}; -``` - -この場合、`feature_hoge` は再帰 patch されず、右辺の object に丸ごと置き換わる。 +object 全体を別構造にしたい場合は、patch 対象より外側で値を作り直す。 ## `&` と `//` の使い分け diff --git a/doc/manual/souce/language/syntax.md b/doc/manual/souce/language/syntax.md index 160b20e..3b2048a 100644 --- a/doc/manual/souce/language/syntax.md +++ b/doc/manual/souce/language/syntax.md @@ -1,7 +1,7 @@ # 構文と字句 この章では、表層構文の方針をまとめる。 -厳密な EBNF は未確定であり、今後このファイルに詳細化する。 +厳密な構文は [Grammar](./grammar.md) の EBNF を参照する。 ## 言語名と拡張子 @@ -93,9 +93,9 @@ config.feature_hoge.enable } ``` -## 予約語候補 +## 予約語 -以下は予約語または予約構文として扱う候補である。 +以下は予約語として扱う。 ```text let @@ -105,11 +105,8 @@ import default true false -rec ``` -`rec` の扱いは未確定である。 - ## 演算子 主要な演算子は以下である。 diff --git a/doc/manual/souce/language/value/float.md b/doc/manual/souce/language/value/float.md index cfc2d7d..acb31ab 100644 --- a/doc/manual/souce/language/value/float.md +++ b/doc/manual/souce/language/value/float.md @@ -9,7 +9,10 @@ ratio = Float; threshold = Float default 0.5; ``` -## 未確定事項 +## Int との関係 -`Int` と `Float` の暗黙変換を許可するかは未確定である。 -軽量実装では、両者を明確に分ける方が単純である。 +`Int` と `Float` は primitive type constraint としては別の型である。 +`Float` constraint は concrete `Float` を要求し、`Int` constraint は concrete `Int` を要求する。 + +数値演算や比較式では `Int` と `Float` を同じ numeric value として扱える。 +混在した四則演算の結果は `Float` になる。 diff --git a/doc/manual/souce/language/value/string.md b/doc/manual/souce/language/value/string.md index 260655e..91e01b6 100644 --- a/doc/manual/souce/language/value/string.md +++ b/doc/manual/souce/language/value/string.md @@ -17,4 +17,5 @@ greeting = String default "hello"; message = String & /Hello! .*/; ``` -正規表現制約を必須機能にするかは未確定である。 +正規表現制約の検証は Rust crate の `regex` feature で有効化される。 +feature が無効な場合、正規表現制約は具体 string に対して検証できず diagnostic になる。 diff --git a/doc/manual/souce/open-issues.md b/doc/manual/souce/open-issues.md index eb32ce2..fac4338 100644 --- a/doc/manual/souce/open-issues.md +++ b/doc/manual/souce/open-issues.md @@ -1,60 +1,23 @@ # 未確定事項 今後決める必要がある事項を管理する。 -詳細化するときは、各項目を該当する仕様ファイルへ移動または反映する。 +実装または仕様方針が固まった項目は、該当する仕様ファイルへ反映してここから外す。 -## 構文 +## Materialize target and host projection -- 正式な字句・構文仕様。 -- 演算子の優先順位。 -- `rec` の扱い。 -- コメント構文を `#` のみにするか。 +Decodal の評価結果は、制約・default・関数値を含む中間値になり得る。 +そのため、Decodal 単独で常に「最終成果物」を一意に決めるのではなく、host 側が期待する型や出力形式を与えて materialize / decode する経路を明確にする必要がある。 -## 型・制約 +決めること: -- 配列要素の制約表現。 -- object の open/closed schema の扱い。 -- 正規表現を必須機能にするか optional feature にするか。 -- 代表的な組み込み述語の範囲。 +- Rust API で評価結果の field/path を選択して decode / materialize できるようにするか。 +- `decodal-derive` の struct schema と評価結果を合成して decode する経路を、主要な materialize path として位置づけるか。 +- CLI / WASM では target path を指定して JSON-compatible value へ materialize する形にするか。 +- 制約や関数値が残った値を出力したい場合、materialize ではなく inspect/debug API として分けるか。 -## default +現時点の案: -- `default` 同士の conflict 解決規則。 -- `&` による default 合成の厳密な規則。 -- `//` による default 置換の厳密な規則。 -- default thunk の評価失敗をどの段階で報告するか。 - -## 演算子 - -- `//` による制約・default の置換詳細。 -- `replace(...)` を採用するか、別構文を設けるか。 -- 配列に対する patch 操作を右辺置換だけにするか。 -- 配列 append / prepend / remove などを提供するか。 - -## 関数 - -- 関数値の最終出力可否。 -- 再帰関数を許可するか。 -- 関数同士の `&` の扱い。 -- 関数値の等価性。 -- 関数呼び出し結果の memoize 範囲。 - -## 評価 - -- thunk のエラー memoize 方針。 -- import cache の単位。 -- 循環 import の診断メッセージ。 -- materialize 対象の範囲指定方法。 - -## match - -- match の網羅性チェックを行うか。 -- 到達不能分岐を警告するか。 -- パターン構文の範囲。 - -## エラー処理 - -- optional import を導入するか。 -- optional field access を導入するか。 -- optional fallback が捕捉できる失敗の範囲。 -- エラー報告に制約由来の説明をどこまで含めるか。 +- Rust では `evaluate -> select field/path -> expected schema と合成 -> decode` を主経路にする。 +- CLI / WASM では、明示された target path または module 全体を JSON-compatible value として materialize する。 +- materialize できない unresolved abstract value、default のない制約値、関数値は diagnostic にする。 +- Decodal 言語内には materialize 構文を追加せず、host API / CLI / WASM の責務として扱う。