Add bilingual manual and localized docs routes
This commit is contained in:
@@ -0,0 +1,250 @@
|
||||
# Operators
|
||||
|
||||
This chapter defines the meaning of Decodal's operators.
|
||||
|
||||
## Operator reference
|
||||
|
||||
| Operator | Form | Kind | Operands | Result / meaning |
|
||||
|---|---|---|---|---|
|
||||
| `.` | `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 |
|
||||
| `as` | `narrower as wider` | range refinement | value / constraint / structure | narrower result plus untouched right-only ranges |
|
||||
|
||||
A concrete scalar is a `String`, `Bool`, `Int`, or `Float` value.
|
||||
|
||||
## Precedence
|
||||
|
||||
Precedence is highest first:
|
||||
|
||||
1. Function calls and field references
|
||||
2. Unary `!` and `-`
|
||||
3. `*` and `/`
|
||||
4. `+` and `-`
|
||||
5. `++`
|
||||
6. `==`, `!=`, `<`, `<=`, `>`, and `>=`
|
||||
7. `&&`
|
||||
8. `||`
|
||||
9. `&`
|
||||
10. `//`
|
||||
11. `default`
|
||||
12. `as`
|
||||
|
||||
Binary operators at the same precedence are left-associative.
|
||||
`default` is right-associative.
|
||||
|
||||
## Arithmetic operators
|
||||
|
||||
`+`, `-`, `*`, and `/` perform arithmetic on concrete `Int` and `Float` values.
|
||||
See [Arithmetic Expression](./expression/arithmetic.md) for details.
|
||||
|
||||
## Array concatenation operator
|
||||
|
||||
`++` concatenates two concrete arrays.
|
||||
It does not transform the elements; elements from the right side follow elements from the left.
|
||||
|
||||
```dcdl
|
||||
["read", "write"] ++ ["admin"]
|
||||
```
|
||||
|
||||
## Logical and comparison operators
|
||||
|
||||
`!`, `&&`, and `||` operate on concrete `Bool` values.
|
||||
`&&` and `||` short-circuit.
|
||||
|
||||
`==` and `!=` compare concrete scalar values.
|
||||
`<`, `<=`, `>`, and `>=` compare concrete numeric values.
|
||||
See [Logical and Comparison Expressions](./expression/logical-and-comparison.md) for details.
|
||||
|
||||
## `&`: constraint-preserving composition
|
||||
|
||||
`&` composes values, constraints, and structures.
|
||||
|
||||
```dcdl
|
||||
A & B
|
||||
```
|
||||
|
||||
The basic rules are:
|
||||
|
||||
- Two constraints become a constraint that satisfies both.
|
||||
- A constraint and a concrete value require the concrete value to satisfy the constraint.
|
||||
- Two identical concrete values produce that value.
|
||||
- Two different concrete values conflict.
|
||||
- Two objects are composed field by field.
|
||||
- A field present on both sides is composed with `&`.
|
||||
- A field present on only one side is retained unchanged.
|
||||
- Any contradiction produces an error.
|
||||
|
||||
For example:
|
||||
|
||||
```dcdl
|
||||
Port = Int & >= 1 & <= 65535;
|
||||
NarrowedPort = Port & > 443;
|
||||
|
||||
MyConfig = {
|
||||
port = NarrowedPort default 8080;
|
||||
};
|
||||
|
||||
Config = MyConfig & {
|
||||
port = 8000;
|
||||
};
|
||||
```
|
||||
|
||||
Conceptually, `Config.port` becomes:
|
||||
|
||||
```text
|
||||
NarrowedPort & 8000
|
||||
```
|
||||
|
||||
This succeeds because `8000` satisfies `NarrowedPort`.
|
||||
The following fails:
|
||||
|
||||
```dcdl
|
||||
BadConfig = MyConfig & {
|
||||
port = 80;
|
||||
};
|
||||
```
|
||||
|
||||
`80` does not satisfy `> 443`.
|
||||
|
||||
## `//`: patch composition
|
||||
|
||||
`//` is a right-biased structural patch operator.
|
||||
Where `&` preserves constraints, `//` changes or overrides configuration and schemas.
|
||||
|
||||
```dcdl
|
||||
A // B
|
||||
```
|
||||
|
||||
The basic rules are:
|
||||
|
||||
- Two objects are patched recursively field by field.
|
||||
- The same field containing object/object is patched recursively.
|
||||
- The same field containing anything other than object/object is replaced by the right side.
|
||||
- Fields present only on the left are retained.
|
||||
- Fields present only on the right are added.
|
||||
- Arrays are replaced by the right side by default.
|
||||
- Function values are replaced by the right side by default.
|
||||
|
||||
Thus, `//` is a deep patch rather than a shallow merge.
|
||||
|
||||
```dcdl
|
||||
Base = {
|
||||
feature_hoge = {
|
||||
enable = Bool default true;
|
||||
fuga = Int default 10;
|
||||
};
|
||||
};
|
||||
|
||||
Patched = Base // {
|
||||
feature_hoge = {
|
||||
enable = false;
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
`Patched` is equivalent to:
|
||||
|
||||
```dcdl
|
||||
{
|
||||
feature_hoge = {
|
||||
enable = false;
|
||||
fuga = Int default 10;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
The same patch can be written with a dot path:
|
||||
|
||||
```dcdl
|
||||
Patched = Base // {
|
||||
feature_hoge.enable = false;
|
||||
};
|
||||
```
|
||||
|
||||
## Replacing an entire object
|
||||
|
||||
Under `//`, object/object is always patched deeply.
|
||||
The core language does not include a special `replace(...)` syntax or built-in function for replacing an entire object field.
|
||||
|
||||
To use an entirely different object structure, construct the value outside the object being patched.
|
||||
|
||||
## `as`: range refinement
|
||||
|
||||
`as` is an asymmetric refinement operator that verifies the left side is more concrete and narrower than the right side.
|
||||
|
||||
```dcdl
|
||||
Refined = {
|
||||
port = 8000;
|
||||
} as {
|
||||
port = Int & >= 1 & <= 65535;
|
||||
host = String;
|
||||
enabled = Bool default true;
|
||||
};
|
||||
```
|
||||
|
||||
`port` becomes the concrete value `8000`.
|
||||
`host` and `enabled`, which exist only on the right, remain abstract in the result. Whether a field has a default does not affect preservation of a right-only field.
|
||||
`as` does not select defaults; normal default rules apply only if the result is later materialized.
|
||||
|
||||
A field present only in the left object lies outside the right-hand field domain and produces an error.
|
||||
Nested objects, array constraints, and map constraints present on both sides are refined recursively using the same rules.
|
||||
|
||||
Containment can also be checked between abstract ranges.
|
||||
|
||||
```dcdl
|
||||
Int & > 10 as Int & > 0 # succeeds and returns Int & > 10
|
||||
Int as Int & > 0 # fails
|
||||
```
|
||||
|
||||
`&` is symmetric and retains object fields found on only one side.
|
||||
Do not use it in place of directional refinement.
|
||||
See [Range Refinement](./expression/ascription.md) for details.
|
||||
|
||||
## Choosing between `&`, `//`, and `as`
|
||||
|
||||
Use `&` for symmetric composition that preserves constraints and partial structure.
|
||||
|
||||
```dcdl
|
||||
Combined = MyConfig & {
|
||||
port = 8000;
|
||||
};
|
||||
```
|
||||
|
||||
Use `//` to override or reshape an existing structure.
|
||||
|
||||
```dcdl
|
||||
ModifiedSchema = MyConfig // {
|
||||
port = Int default 9000;
|
||||
};
|
||||
```
|
||||
|
||||
Because `//` is a right-biased patch, it does not always preserve constraints from the left.
|
||||
Use `&` when those constraints must remain.
|
||||
|
||||
Use `as` to compose while verifying that the left side is narrower than the right.
|
||||
|
||||
```dcdl
|
||||
RefinedConfig = NarrowConfig as WideConfig;
|
||||
```
|
||||
Reference in New Issue
Block a user