# 演算子 この章では、Decodal の演算子の意味を定義する。 ## 演算子一覧 | 演算子 | 形 | 種類 | 対象 | 結果 / 意味 | |---|---|---|---|---| | `.` | `object.field` | field reference | object / abstract object | field value | | call | `fn(arg)` | function call | function | function result | | `!` | `!expr` | unary logical | concrete `Bool` | concrete `Bool` | | `-` | `-expr` | unary arithmetic | concrete `Int` / `Float` | negated number | | `*` | `lhs * rhs` | arithmetic | concrete `Int` / `Float` | numeric product | | `/` | `lhs / rhs` | arithmetic | concrete `Int` / `Float` | `Float` quotient | | `+` | `lhs + rhs` | arithmetic | concrete `Int` / `Float` | numeric sum | | `-` | `lhs - rhs` | arithmetic | concrete `Int` / `Float` | numeric difference | | `++` | `lhs ++ rhs` | array concat | concrete arrays | concatenated array | | `==` | `lhs == rhs` | equality | concrete scalar | concrete `Bool` | | `!=` | `lhs != rhs` | equality | concrete scalar | concrete `Bool` | | `<` | `lhs < rhs` | ordering | concrete `Int` / `Float` | concrete `Bool` | | `<=` | `lhs <= rhs` | ordering | concrete `Int` / `Float` | concrete `Bool` | | `>` | `lhs > rhs` | ordering | concrete `Int` / `Float` | concrete `Bool` | | `>=` | `lhs >= rhs` | ordering | concrete `Int` / `Float` | concrete `Bool` | | `>` | `> value` | comparison constraint | numeric constraint value | abstract constraint | | `>=` | `>= value` | comparison constraint | numeric constraint value | abstract constraint | | `<` | `< value` | comparison constraint | numeric constraint value | abstract constraint | | `<=` | `<= value` | comparison constraint | numeric constraint value | abstract constraint | | `&&` | `lhs && rhs` | logical | concrete `Bool` | short-circuit AND | | `||` | `lhs || rhs` | logical | concrete `Bool` | short-circuit OR | | `&` | `lhs & rhs` | composition | value / constraint / object | constraint-preserving composition | | `//` | `lhs // rhs` | patch | object / value | right-biased structural patch | | `default` | `base default fallback` | default | abstract value | materialization fallback | `concrete scalar` は `String`、`Bool`、`Int`、`Float` を指す。 ## 優先順位 優先順位は高い順に以下である。 1. 関数呼び出しとフィールド参照 2. unary `!` `-` 3. `*` `/` 4. `+` `-` 5. `++` 6. `==` `!=` `<` `<=` `>` `>=` 7. `&&` 8. `||` 9. `&` 10. `//` 11. `default` 同じ優先順位の二項演算子は左結合である。 `default` は右結合である。 ## Arithmetic operators `+` `-` `*` `/` は具体的な `Int` / `Float` に対する四則演算である。 詳しくは [Arithmetic Expression](./expression/arithmetic.md) を参照する。 ## Array concat operator `++` は concrete array 同士を連結する演算子である。 要素は変換されず、左辺の要素の後に右辺の要素が並ぶ。 ```dcdl ["read", "write"] ++ ["admin"] ``` ## Logical and comparison operators `!` `&&` `||` は concrete `Bool` に対する論理演算である。 `&&` と `||` は短絡評価される。 `==` `!=` は concrete scalar value を比較する。 `<` `<=` `>` `>=` は concrete numeric value を比較する。 詳しくは [Logical and Comparison Expressions](./expression/logical-and-comparison.md) を参照する。 ## `&`: 制約合成 `&` は値・制約・構造を合成する演算子である。 ```dcdl A & B ``` 基本規則: - 両方が制約なら、両方を満たす制約になる。 - 制約と具体値なら、具体値が制約を満たす必要がある。 - 両方が同じ具体値なら、その値になる。 - 両方が異なる具体値なら conflict になる。 - 両方が object なら、フィールドごとに合成する。 - 同じフィールドが両方にある場合、そのフィールド値を `&` で合成する。 - 矛盾が発生した場合はエラーになる。 例: ```dcdl Port = Int & >= 1 & <= 65535; NarrowedPort = Port & > 443; MyConfig = { port = NarrowedPort default 8080; }; Config = MyConfig & { port = 8000; }; ``` `Config.port` は概念的には以下になる。 ```text NarrowedPort & 8000 ``` `8000` は `NarrowedPort` を満たすため成功する。 一方、以下は失敗する。 ```dcdl BadConfig = MyConfig & { port = 80; }; ``` `80` は `> 443` を満たさないためである。 ## `//`: patch 合成 `//` は右辺優先の構造的 patch 演算子である。 `&` が制約を保った合成であるのに対し、`//` は設定やスキーマを上書き・変更するために使う。 ```dcdl A // B ``` 基本規則: - 両方が object なら、フィールドごとに再帰的に patch する。 - 同じフィールドが object/object なら、さらに再帰的に patch する。 - 同じフィールドが object/object 以外なら、右辺で置き換える。 - 左辺にしかないフィールドは保持する。 - 右辺にしかないフィールドは追加する。 - 配列はデフォルトでは右辺で置き換える。 - 関数値はデフォルトでは右辺で置き換える。 つまり `//` は shallow merge ではなく deep patch とする。 ```dcdl Base = { feature_hoge = { enable = Bool default true; fuga = Int default 10; }; }; Patched = Base // { feature_hoge = { enable = false; }; }; ``` `Patched` は以下に相当する。 ```dcdl { feature_hoge = { enable = false; fuga = Int default 10; }; } ``` ドットパスを使うと以下のようにも書ける。 ```dcdl Patched = Base // { feature_hoge.enable = false; }; ``` ## object 全体の置換 `//` が deep patch である場合、object 全体を置き換えたいときの escape hatch が必要になる。 候補として、`replace(...)` を組み込み関数として提供する。 ```dcdl Replaced = Base // { feature_hoge = replace({ enable = false; }); }; ``` この場合、`feature_hoge` は再帰 patch されず、右辺の object に丸ごと置き換わる。 ## `&` と `//` の使い分け `&` は制約を満たす具体化に使う。 ```dcdl ValidConfig = MyConfig & { port = 8000; }; ``` `//` は既存構造の上書きや変形に使う。 ```dcdl ModifiedSchema = MyConfig // { port = Int default 9000; }; ``` `//` は右辺優先の patch であり、左辺の制約を常に保持するとは限らない。 制約を保持したい場合は `&` を使う。