Refocus manual on public language usage

This commit is contained in:
2026-08-14 08:17:25 +09:00
parent e0a9a8efb7
commit e0970acdcc
41 changed files with 611 additions and 1982 deletions
@@ -1,10 +1,6 @@
# 制約と default
この章では、制約と `default` の意味を定義する
## 制約
制約は、値が満たすべき条件を表す。
constraint は、値が満たすべき範囲を表す
```dcdl
Int
@@ -14,148 +10,104 @@ String
/Hello! .*/
```
制約`&` により合成できる。
constraint `&` 合成できる。
```dcdl
Port = Int & >= 1 & <= 65535;
NarrowedPort = Port & > 443;
```
制約合成の意味は、すべての制約を同時に満たすことである。
```text
A & B = A と B の両方を満たす値または制約
```
矛盾する制約はエラーになる。
```dcdl
Int & String # エラー
> 10 & < 5 # エラー
Int & > 10 & < 11 # エラー。整数値の候補が存在しない
```
## 制約の正規化
`&` によって abstract value 同士を合成した場合、処理系は軽量に判定できる制約を正規化する。
正規化対象:
- primitive type 制約。
- 数値比較制約。
primitive type 制約は、異なる型が同時に要求された場合 conflict になる。
```dcdl
Int & Float
Int & String
```
数値比較制約は上下限として正規化される。
```dcdl
Int & >= 1 & <= 65535 & > 443
```
これは概念的に以下へ正規化される。
```text
Type(Int)
> 443
<= 65535
```
上下限の交差が空であれば conflict になる。
`Int` 制約がある場合は、整数候補が存在するかも判定する。
合成結果は両辺を同時に満たす範囲になる。
両立しない constraint は conflict になる。
```dcdl
Int & String # conflict
> 10 & < 5 # conflict
Int & > 10 & < 11 # conflict
Int & >= 10 & <= 10 # OK
Int & > 10 & < 11 # conflict: integer candidate does not exist
```
`Int` の比較制約は整数リテラルを使う。
`Float` の比較制約は整数リテラルまたは浮動小数リテラルを使える。
## Primitive and comparison constraints
## 組み込み制約
最小の組み込み制約は以下である。
組み込みの primitive range は次の通りである。
```dcdl
Unknown
String
Int
Float
Bool
```
`Unknown` はすべてのDecodal値を含む最上位rangeである。検査を無効化する `Any` ではなく、具体的な値またはより狭いrangeがまだ決まっていないことを表す
異なる primitive type を `&` で合成すると conflict になる
数値比較 constraint は上下限として合成され、空の範囲になる場合は conflict になる。
```dcdl
Int & >= 1 & <= 65535 & > 443
```
`Int` の比較 constraint は integer literal を使う。
`Float` の比較 constraint は integer または float literal を使える。
## Unknown
`Unknown` はすべての Decodal value を含む最上位 range である。
検査を無効化して具体値の情報を消す `Any` ではなく、値の範囲がまだ絞られていないことを表す。
```dcdl
Int & Unknown # Int
42 as Unknown # 42
Unknown as Int # エラー
Unknown as Int # conflict
```
`Unknown` 自体は具体値を持たないためmaterializeできない。defaultを与えるか、具体値で絞り込む必要がある。
`Unknown` 自体は concrete value を持たないため materialize できない。
concrete value で絞り込むか `default` を指定する必要がある。
```dcdl
Unknown default {} # {}
Unknown default {}
```
追加の述語制約はライブラリまたは組み込みとして提供できる。
## Regex constraints
regex literal は string constraint である。
```dcdl
IPv4Address
Host = /^api-[0-9]+$/;
```
## 正規表現制約
正規表現リテラルは文字列制約として使える。
```dcdl
Host = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/;
```
正規表現制約は積み重ね可能である。
複数の正規表現制約が同じ abstract value に付与された場合、具体文字列はすべての正規表現制約に一致しなければならない。
複数の regex constraint を合成した場合、concrete string はすべてに一致する必要がある。
```dcdl
String & /^a/ & /z$/
```
処理系は、正規表現制約同士の交差が空であるかを合成時に判定する必要はない。
つまり、以下は合成時には conflict にならず、具体値検証時に失敗する。
regex constraint 同士の交差は合成時に判定されない。
そのため、次の範囲は合成時には conflict にならず、concrete value の検証時に失敗する。
```dcdl
String & /^a$/ & /^b$/
```
正規表現エンジンは optional feature にできる。
正規表現 feature が無効な処理系では、正規表現制約の検証は unsupported feature diagnostic になる。
軽量実装では代表的な制約を組み込み述語として提供してもよい。
Rust runtime では regex engine を `regex` feature で有効にする。
feature が無効な場合、regex constraint の concrete value 検証は unsupported feature diagnostic になる。
```dcdl
Host = IPv4Address;
```
## Array constraints
## 配列制約
配列制約には要素制約が必須であり、`[...T]` と書く。
array constraint は `[...T]` と書き、すべての要素へ `T` を適用する。
要素 constraint は必須である。
```dcdl
Names = [...String];
PositiveInts = [...(Int & > 0)];
```
複数の配列制約を `&` で合成した場合、各 concrete 要素をすべての要素 range で絞り込
要素が object range の場合、右辺にしかない field は default の有無にかかわらず abstract のまま各要素へ残る
左辺にしかない field は右辺の field domain 外なのでエラーになる。
要素制約のない `Array` primitive type は存在しない。
concrete array と合成した場合、各要素は `T` に対して `as` と同じ規則で絞り込まれる
空 array は任意の array constraint を満たす
## 連想配列制約
要素 constraint が object range の場合、右辺にだけある field は abstract のまま各要素へ残る。
左辺にしかない field は右辺の field domain 外なので conflict になる。
連想配列制約は `{...T}` と書き、object の任意の field value を `T` に対して絞り込む。
## Associative-array and object rest constraints
associative-array constraint は `{...T}` と書き、object の任意の field value へ `T` を適用する。
```dcdl
Services = {...{
@@ -164,8 +116,8 @@ Services = {...{
}};
```
key は schema で列挙せず、空 object も許容る。
固定 object field と任意 key の value constraint は、末尾 `...T` で混在できる。
key は列挙されず、空 object も許容される。
named field を持つ object の末尾 `...T` を書くと、列挙されていない field だけに `T` を適用できる。
```dcdl
{
@@ -174,78 +126,38 @@ key は schema で列挙せず、空 object も許容する。
}
```
このrest constraintは明示されていないfieldだけに適用され、field自体は生成しない。
rest constraintfield生成しない。
実在する追加 field の検証にだけ使われる。
## default
`default`制約ではない。
`default` は、最終評価時に明示値が存在しない場合だけ使われる fallback である。
`default` constraint ではない。
materialize 時に concrete value がない場合だけ使われる fallback である。
```dcdl
port = NarrowedPort default 8080;
```
Port = Int & >= 1 & <= 65535;
これは概念的には以下を表す。
```text
Abstract {
constraints: [NarrowedPort]
default: 8080
}
```
明示値が合成された場合、`default` は採用されない。
```dcdl
MyConfig = {
port = NarrowedPort default 8080;
};
Config = MyConfig & {
port = 8000;
Service = {
port = Port default 8080;
};
```
この場合、最終値は `8000` である
`8080` は評価されない、または評価されても採用されない。
明示値が合成された場合は明示値が使われ、`default` は評価されない
明示値がない場合、最終 materialize 時に `default` が採用される。
採用された default 値は、同じフィールドに定義された制約を満たす必要がある。
```text
Abstract {
constraints: [NarrowedPort]
default: 8080
}
finalize => 8080 が NarrowedPort を満たせば成功
```dcdl
Config = {
port = 9000;
} as Service;
```
## default の内部表現
明示値がない場合、materialize 時に `8080` が採用され、`Port` を満たすか検証される。
`default` は abstract value に付随する fallback thunk として保持できる。
これにより、default 値自体も必要になるまで評価しない。
## default composition
```text
RuntimeValue =
Concrete(ConcreteValue)
Abstract {
constraints: Vec<Constraint>
default: Option<Thunk>
}
```
- `&` で片方だけが `default` を持つ場合、その `default` は保持される。
- `&` で両辺が異なる `default` を持つ場合は conflict になる。
- constraint と concrete value の `&` が成功した場合、結果は concrete value になり `default` は残らない。
- `//` は右辺優先なので、同じ field では右辺の値または `default` が左辺を置き換える。
- `as` は右辺の `default` を左辺へ注入しない。ただし右辺にだけ存在する field は、その field が持つ `default` とともに abstract なまま結果へ残る。
明示値は `Concrete` として表現し、`default` を保持しない
`Abstract & Concrete` が成功した場合、制約検証後に `Concrete` になり、default は消える。
## 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 に残っている制約を満たす必要がある。
`default` expression は materialize 時に必要になった時点で評価され、同じ range の constraint を満たす必要がある