@rcrsr/rill 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +187 -0
- package/dist/cli.d.ts +11 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +69 -0
- package/dist/cli.js.map +1 -0
- package/dist/demo.d.ts +6 -0
- package/dist/demo.d.ts.map +1 -0
- package/dist/demo.js +121 -0
- package/dist/demo.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/lexer/errors.d.ts +9 -0
- package/dist/lexer/errors.d.ts.map +1 -0
- package/dist/lexer/errors.js +12 -0
- package/dist/lexer/errors.js.map +1 -0
- package/dist/lexer/helpers.d.ts +14 -0
- package/dist/lexer/helpers.d.ts.map +1 -0
- package/dist/lexer/helpers.js +30 -0
- package/dist/lexer/helpers.js.map +1 -0
- package/dist/lexer/index.d.ts +8 -0
- package/dist/lexer/index.d.ts.map +1 -0
- package/dist/lexer/index.js +8 -0
- package/dist/lexer/index.js.map +1 -0
- package/dist/lexer/operators.d.ts +11 -0
- package/dist/lexer/operators.d.ts.map +1 -0
- package/dist/lexer/operators.js +58 -0
- package/dist/lexer/operators.js.map +1 -0
- package/dist/lexer/readers.d.ts +12 -0
- package/dist/lexer/readers.d.ts.map +1 -0
- package/dist/lexer/readers.js +144 -0
- package/dist/lexer/readers.js.map +1 -0
- package/dist/lexer/state.d.ts +18 -0
- package/dist/lexer/state.d.ts.map +1 -0
- package/dist/lexer/state.js +37 -0
- package/dist/lexer/state.js.map +1 -0
- package/dist/lexer/tokenizer.d.ts +9 -0
- package/dist/lexer/tokenizer.d.ts.map +1 -0
- package/dist/lexer/tokenizer.js +100 -0
- package/dist/lexer/tokenizer.js.map +1 -0
- package/dist/lexer.d.ts +19 -0
- package/dist/lexer.d.ts.map +1 -0
- package/dist/lexer.js +344 -0
- package/dist/lexer.js.map +1 -0
- package/dist/parser/arithmetic.d.ts +16 -0
- package/dist/parser/arithmetic.d.ts.map +1 -0
- package/dist/parser/arithmetic.js +128 -0
- package/dist/parser/arithmetic.js.map +1 -0
- package/dist/parser/boolean.d.ts +15 -0
- package/dist/parser/boolean.d.ts.map +1 -0
- package/dist/parser/boolean.js +20 -0
- package/dist/parser/boolean.js.map +1 -0
- package/dist/parser/control-flow.d.ts +56 -0
- package/dist/parser/control-flow.d.ts.map +1 -0
- package/dist/parser/control-flow.js +167 -0
- package/dist/parser/control-flow.js.map +1 -0
- package/dist/parser/expressions.d.ts +23 -0
- package/dist/parser/expressions.d.ts.map +1 -0
- package/dist/parser/expressions.js +950 -0
- package/dist/parser/expressions.js.map +1 -0
- package/dist/parser/extraction.d.ts +48 -0
- package/dist/parser/extraction.d.ts.map +1 -0
- package/dist/parser/extraction.js +279 -0
- package/dist/parser/extraction.js.map +1 -0
- package/dist/parser/functions.d.ts +20 -0
- package/dist/parser/functions.d.ts.map +1 -0
- package/dist/parser/functions.js +96 -0
- package/dist/parser/functions.js.map +1 -0
- package/dist/parser/helpers.d.ts +94 -0
- package/dist/parser/helpers.d.ts.map +1 -0
- package/dist/parser/helpers.js +225 -0
- package/dist/parser/helpers.js.map +1 -0
- package/dist/parser/index.d.ts +49 -0
- package/dist/parser/index.d.ts.map +1 -0
- package/dist/parser/index.js +73 -0
- package/dist/parser/index.js.map +1 -0
- package/dist/parser/literals.d.ts +37 -0
- package/dist/parser/literals.d.ts.map +1 -0
- package/dist/parser/literals.js +373 -0
- package/dist/parser/literals.js.map +1 -0
- package/dist/parser/parser-collect.d.ts +16 -0
- package/dist/parser/parser-collect.d.ts.map +1 -0
- package/dist/parser/parser-collect.js +125 -0
- package/dist/parser/parser-collect.js.map +1 -0
- package/dist/parser/parser-control.d.ts +20 -0
- package/dist/parser/parser-control.d.ts.map +1 -0
- package/dist/parser/parser-control.js +120 -0
- package/dist/parser/parser-control.js.map +1 -0
- package/dist/parser/parser-expr.d.ts +37 -0
- package/dist/parser/parser-expr.d.ts.map +1 -0
- package/dist/parser/parser-expr.js +639 -0
- package/dist/parser/parser-expr.js.map +1 -0
- package/dist/parser/parser-extract.d.ts +17 -0
- package/dist/parser/parser-extract.d.ts.map +1 -0
- package/dist/parser/parser-extract.js +222 -0
- package/dist/parser/parser-extract.js.map +1 -0
- package/dist/parser/parser-functions.d.ts +21 -0
- package/dist/parser/parser-functions.d.ts.map +1 -0
- package/dist/parser/parser-functions.js +155 -0
- package/dist/parser/parser-functions.js.map +1 -0
- package/dist/parser/parser-literals.d.ts +22 -0
- package/dist/parser/parser-literals.d.ts.map +1 -0
- package/dist/parser/parser-literals.js +288 -0
- package/dist/parser/parser-literals.js.map +1 -0
- package/dist/parser/parser-script.d.ts +21 -0
- package/dist/parser/parser-script.d.ts.map +1 -0
- package/dist/parser/parser-script.js +174 -0
- package/dist/parser/parser-script.js.map +1 -0
- package/dist/parser/parser-variables.d.ts +20 -0
- package/dist/parser/parser-variables.d.ts.map +1 -0
- package/dist/parser/parser-variables.js +146 -0
- package/dist/parser/parser-variables.js.map +1 -0
- package/dist/parser/parser.d.ts +49 -0
- package/dist/parser/parser.d.ts.map +1 -0
- package/dist/parser/parser.js +54 -0
- package/dist/parser/parser.js.map +1 -0
- package/dist/parser/script.d.ts +14 -0
- package/dist/parser/script.d.ts.map +1 -0
- package/dist/parser/script.js +196 -0
- package/dist/parser/script.js.map +1 -0
- package/dist/parser/state.d.ts +40 -0
- package/dist/parser/state.d.ts.map +1 -0
- package/dist/parser/state.js +129 -0
- package/dist/parser/state.js.map +1 -0
- package/dist/parser/variables.d.ts +10 -0
- package/dist/parser/variables.d.ts.map +1 -0
- package/dist/parser/variables.js +215 -0
- package/dist/parser/variables.js.map +1 -0
- package/dist/runtime/ast-equals.d.ts +13 -0
- package/dist/runtime/ast-equals.d.ts.map +1 -0
- package/dist/runtime/ast-equals.js +447 -0
- package/dist/runtime/ast-equals.js.map +1 -0
- package/dist/runtime/builtins.d.ts +13 -0
- package/dist/runtime/builtins.d.ts.map +1 -0
- package/dist/runtime/builtins.js +180 -0
- package/dist/runtime/builtins.js.map +1 -0
- package/dist/runtime/callable.d.ts +88 -0
- package/dist/runtime/callable.d.ts.map +1 -0
- package/dist/runtime/callable.js +98 -0
- package/dist/runtime/callable.js.map +1 -0
- package/dist/runtime/context.d.ts +13 -0
- package/dist/runtime/context.d.ts.map +1 -0
- package/dist/runtime/context.js +73 -0
- package/dist/runtime/context.js.map +1 -0
- package/dist/runtime/core/callable.d.ts +171 -0
- package/dist/runtime/core/callable.d.ts.map +1 -0
- package/dist/runtime/core/callable.js +246 -0
- package/dist/runtime/core/callable.js.map +1 -0
- package/dist/runtime/core/context.d.ts +29 -0
- package/dist/runtime/core/context.d.ts.map +1 -0
- package/dist/runtime/core/context.js +154 -0
- package/dist/runtime/core/context.js.map +1 -0
- package/dist/runtime/core/equals.d.ts +9 -0
- package/dist/runtime/core/equals.d.ts.map +1 -0
- package/dist/runtime/core/equals.js +381 -0
- package/dist/runtime/core/equals.js.map +1 -0
- package/dist/runtime/core/eval/base.d.ts +65 -0
- package/dist/runtime/core/eval/base.d.ts.map +1 -0
- package/dist/runtime/core/eval/base.js +112 -0
- package/dist/runtime/core/eval/base.js.map +1 -0
- package/dist/runtime/core/eval/evaluator.d.ts +47 -0
- package/dist/runtime/core/eval/evaluator.d.ts.map +1 -0
- package/dist/runtime/core/eval/evaluator.js +73 -0
- package/dist/runtime/core/eval/evaluator.js.map +1 -0
- package/dist/runtime/core/eval/index.d.ts +57 -0
- package/dist/runtime/core/eval/index.d.ts.map +1 -0
- package/dist/runtime/core/eval/index.js +95 -0
- package/dist/runtime/core/eval/index.js.map +1 -0
- package/dist/runtime/core/eval/mixins/annotations.d.ts +19 -0
- package/dist/runtime/core/eval/mixins/annotations.d.ts.map +1 -0
- package/dist/runtime/core/eval/mixins/annotations.js +146 -0
- package/dist/runtime/core/eval/mixins/annotations.js.map +1 -0
- package/dist/runtime/core/eval/mixins/closures.d.ts +49 -0
- package/dist/runtime/core/eval/mixins/closures.d.ts.map +1 -0
- package/dist/runtime/core/eval/mixins/closures.js +479 -0
- package/dist/runtime/core/eval/mixins/closures.js.map +1 -0
- package/dist/runtime/core/eval/mixins/collections.d.ts +24 -0
- package/dist/runtime/core/eval/mixins/collections.d.ts.map +1 -0
- package/dist/runtime/core/eval/mixins/collections.js +466 -0
- package/dist/runtime/core/eval/mixins/collections.js.map +1 -0
- package/dist/runtime/core/eval/mixins/control-flow.d.ts +27 -0
- package/dist/runtime/core/eval/mixins/control-flow.d.ts.map +1 -0
- package/dist/runtime/core/eval/mixins/control-flow.js +369 -0
- package/dist/runtime/core/eval/mixins/control-flow.js.map +1 -0
- package/dist/runtime/core/eval/mixins/core.d.ts +24 -0
- package/dist/runtime/core/eval/mixins/core.d.ts.map +1 -0
- package/dist/runtime/core/eval/mixins/core.js +335 -0
- package/dist/runtime/core/eval/mixins/core.js.map +1 -0
- package/dist/runtime/core/eval/mixins/expressions.d.ts +19 -0
- package/dist/runtime/core/eval/mixins/expressions.d.ts.map +1 -0
- package/dist/runtime/core/eval/mixins/expressions.js +202 -0
- package/dist/runtime/core/eval/mixins/expressions.js.map +1 -0
- package/dist/runtime/core/eval/mixins/extraction.d.ts +10 -0
- package/dist/runtime/core/eval/mixins/extraction.d.ts.map +1 -0
- package/dist/runtime/core/eval/mixins/extraction.js +250 -0
- package/dist/runtime/core/eval/mixins/extraction.js.map +1 -0
- package/dist/runtime/core/eval/mixins/literals.d.ts +23 -0
- package/dist/runtime/core/eval/mixins/literals.d.ts.map +1 -0
- package/dist/runtime/core/eval/mixins/literals.js +180 -0
- package/dist/runtime/core/eval/mixins/literals.js.map +1 -0
- package/dist/runtime/core/eval/mixins/types.d.ts +20 -0
- package/dist/runtime/core/eval/mixins/types.d.ts.map +1 -0
- package/dist/runtime/core/eval/mixins/types.js +109 -0
- package/dist/runtime/core/eval/mixins/types.js.map +1 -0
- package/dist/runtime/core/eval/mixins/variables.d.ts +34 -0
- package/dist/runtime/core/eval/mixins/variables.d.ts.map +1 -0
- package/dist/runtime/core/eval/mixins/variables.js +247 -0
- package/dist/runtime/core/eval/mixins/variables.js.map +1 -0
- package/dist/runtime/core/eval/types.d.ts +41 -0
- package/dist/runtime/core/eval/types.d.ts.map +1 -0
- package/dist/runtime/core/eval/types.js +10 -0
- package/dist/runtime/core/eval/types.js.map +1 -0
- package/dist/runtime/core/evaluate.d.ts +42 -0
- package/dist/runtime/core/evaluate.d.ts.map +1 -0
- package/dist/runtime/core/evaluate.debug.js +1251 -0
- package/dist/runtime/core/evaluate.js +1913 -0
- package/dist/runtime/core/evaluate.js.map +1 -0
- package/dist/runtime/core/execute.d.ts +26 -0
- package/dist/runtime/core/execute.d.ts.map +1 -0
- package/dist/runtime/core/execute.js +177 -0
- package/dist/runtime/core/execute.js.map +1 -0
- package/dist/runtime/core/signals.d.ts +19 -0
- package/dist/runtime/core/signals.d.ts.map +1 -0
- package/dist/runtime/core/signals.js +26 -0
- package/dist/runtime/core/signals.js.map +1 -0
- package/dist/runtime/core/types.d.ts +177 -0
- package/dist/runtime/core/types.d.ts.map +1 -0
- package/dist/runtime/core/types.js +50 -0
- package/dist/runtime/core/types.js.map +1 -0
- package/dist/runtime/core/values.d.ts +66 -0
- package/dist/runtime/core/values.d.ts.map +1 -0
- package/dist/runtime/core/values.js +240 -0
- package/dist/runtime/core/values.js.map +1 -0
- package/dist/runtime/evaluate.d.ts +32 -0
- package/dist/runtime/evaluate.d.ts.map +1 -0
- package/dist/runtime/evaluate.js +1111 -0
- package/dist/runtime/evaluate.js.map +1 -0
- package/dist/runtime/execute.d.ts +26 -0
- package/dist/runtime/execute.d.ts.map +1 -0
- package/dist/runtime/execute.js +121 -0
- package/dist/runtime/execute.js.map +1 -0
- package/dist/runtime/ext/builtins.d.ts +16 -0
- package/dist/runtime/ext/builtins.d.ts.map +1 -0
- package/dist/runtime/ext/builtins.js +528 -0
- package/dist/runtime/ext/builtins.js.map +1 -0
- package/dist/runtime/ext/content-parser.d.ts +83 -0
- package/dist/runtime/ext/content-parser.d.ts.map +1 -0
- package/dist/runtime/ext/content-parser.js +536 -0
- package/dist/runtime/ext/content-parser.js.map +1 -0
- package/dist/runtime/index.d.ts +28 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +34 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/signals.d.ts +19 -0
- package/dist/runtime/signals.d.ts.map +1 -0
- package/dist/runtime/signals.js +26 -0
- package/dist/runtime/signals.js.map +1 -0
- package/dist/runtime/types.d.ts +169 -0
- package/dist/runtime/types.d.ts.map +1 -0
- package/dist/runtime/types.js +50 -0
- package/dist/runtime/types.js.map +1 -0
- package/dist/runtime/values.d.ts +50 -0
- package/dist/runtime/values.d.ts.map +1 -0
- package/dist/runtime/values.js +209 -0
- package/dist/runtime/values.js.map +1 -0
- package/dist/runtime.d.ts +254 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +2014 -0
- package/dist/runtime.js.map +1 -0
- package/dist/types.d.ts +752 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +189 -0
- package/dist/types.js.map +1 -0
- package/docs/00_INDEX.md +65 -0
- package/docs/01_guide.md +390 -0
- package/docs/02_types.md +399 -0
- package/docs/03_variables.md +314 -0
- package/docs/04_operators.md +551 -0
- package/docs/05_control-flow.md +350 -0
- package/docs/06_closures.md +353 -0
- package/docs/07_collections.md +686 -0
- package/docs/08_iterators.md +330 -0
- package/docs/09_strings.md +205 -0
- package/docs/10_parsing.md +366 -0
- package/docs/11_reference.md +350 -0
- package/docs/12_examples.md +771 -0
- package/docs/13_modules.md +519 -0
- package/docs/14_host-integration.md +826 -0
- package/docs/15_grammar.ebnf +693 -0
- package/docs/16_conventions.md +696 -0
- package/docs/99_llm-reference.txt +300 -0
- package/docs/assets/logo.png +0 -0
- package/package.json +70 -0
package/docs/02_types.md
ADDED
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
# rill Type System
|
|
2
|
+
|
|
3
|
+
*Value types, type assertions, and type checking*
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
rill is dynamically typed and type-safe. Types are checked at runtime, but type errors are always caught—there are no implicit conversions or coercions.
|
|
8
|
+
|
|
9
|
+
| Type | Syntax | Example |
|
|
10
|
+
|------|--------|---------|
|
|
11
|
+
| String | `"text"` | `"hello"` |
|
|
12
|
+
| Number | `123`, `0.5` | `42`, `0.9` |
|
|
13
|
+
| Bool | `true`, `false` | `true` |
|
|
14
|
+
| List | `[a, b]` | `["file.ts", 42]` |
|
|
15
|
+
| Dict | `[k: v]` | `[output: "text", code: 0]` |
|
|
16
|
+
| Tuple | `*[...]` | `*[1, 2]`, `*[x: 1, y: 2]` |
|
|
17
|
+
| Closure | `\|\|{ }` | `\|x\|($x * 2)` |
|
|
18
|
+
|
|
19
|
+
**Key principles:**
|
|
20
|
+
- **Type-safe**: No implicit coercion—`"5" + 1` errors, not `"51"` or `6`
|
|
21
|
+
- **Type-locked variables**: A variable that holds a string always holds a string
|
|
22
|
+
- **Value-based**: All copies are deep, all comparisons by value
|
|
23
|
+
- **No null/undefined**: Empty values are valid (`""`, `[]`, `[:]`), but "no value" cannot exist
|
|
24
|
+
- **No truthiness**: Conditions require actual booleans, not "truthy" values
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Strings
|
|
29
|
+
|
|
30
|
+
Double-quoted text with variable interpolation using `{$var}`:
|
|
31
|
+
|
|
32
|
+
```text
|
|
33
|
+
"hello world"
|
|
34
|
+
"Process {$filename} for review"
|
|
35
|
+
"Result: {$response}"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Escape sequences: `\n`, `\t`, `\\`, `\"`, `{{` (literal `{`), `}}` (literal `}`)
|
|
39
|
+
|
|
40
|
+
### Interpolation
|
|
41
|
+
|
|
42
|
+
Any valid expression works inside `{...}`:
|
|
43
|
+
|
|
44
|
+
```rill
|
|
45
|
+
"alice" :> $name
|
|
46
|
+
3 :> $a
|
|
47
|
+
5 :> $b
|
|
48
|
+
true :> $ok
|
|
49
|
+
"Hello, {$name}!" # Variable
|
|
50
|
+
"sum: {$a + $b}" # Arithmetic
|
|
51
|
+
"valid: {$a > 0}" # Comparison
|
|
52
|
+
"status: {$ok ? \"yes\" ! \"no\"}" # Conditional
|
|
53
|
+
"upper: {$name -> .upper}" # Method chain
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Heredocs
|
|
57
|
+
|
|
58
|
+
Multiline strings use heredoc syntax:
|
|
59
|
+
|
|
60
|
+
```rill
|
|
61
|
+
prompt(<<EOF
|
|
62
|
+
Review this code:
|
|
63
|
+
{$code}
|
|
64
|
+
|
|
65
|
+
Check for security issues.
|
|
66
|
+
EOF
|
|
67
|
+
)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The delimiter (e.g., `EOF`) must not appear on its own line within the content.
|
|
71
|
+
|
|
72
|
+
See [Strings](09_strings.md) for string methods.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Numbers
|
|
77
|
+
|
|
78
|
+
Used for arithmetic, exit codes, and loop limits:
|
|
79
|
+
|
|
80
|
+
```rill
|
|
81
|
+
42
|
|
82
|
+
0
|
|
83
|
+
3.14159
|
|
84
|
+
-7
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**Arithmetic operators:** `+`, `-`, `*`, `/`, `%` (modulo)
|
|
88
|
+
|
|
89
|
+
**Type constraint:** All arithmetic operands must be numbers. No implicit conversion:
|
|
90
|
+
|
|
91
|
+
```rill
|
|
92
|
+
5 + 3 # 8
|
|
93
|
+
"5" + 1 # ERROR: Arithmetic requires number, got string
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Booleans
|
|
99
|
+
|
|
100
|
+
Literal `true` and `false`. Conditional expressions (`?`), loop conditions (`@`), and filter predicates require boolean values. Non-boolean values cause runtime errors.
|
|
101
|
+
|
|
102
|
+
```rill
|
|
103
|
+
true ? "yes" ! "no" # "yes"
|
|
104
|
+
false ? "yes" ! "no" # "no"
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**No truthiness:** rill has no automatic boolean coercion. Empty strings, zero, and empty lists are not "falsy"—you must explicitly check:
|
|
108
|
+
|
|
109
|
+
```rill
|
|
110
|
+
"" -> .empty ? "empty" ! "has content" # Use .empty method
|
|
111
|
+
0 -> ($ == 0) ? "zero" ! "nonzero" # Use comparison
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Lists
|
|
117
|
+
|
|
118
|
+
Ordered sequences of values:
|
|
119
|
+
|
|
120
|
+
```rill
|
|
121
|
+
[1, 2, 3] :> $nums
|
|
122
|
+
$nums[0] # 1
|
|
123
|
+
$nums[-1] # 3 (last element)
|
|
124
|
+
$nums -> .len # 3
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**Access methods:**
|
|
128
|
+
- `[0]`, `[1]` — Index access (0-based)
|
|
129
|
+
- `[-1]`, `[-2]` — Negative index (from end)
|
|
130
|
+
- `.head` — First element (errors on empty)
|
|
131
|
+
- `.tail` — Last element (errors on empty)
|
|
132
|
+
- `.at(n)` — Element at index
|
|
133
|
+
|
|
134
|
+
**Out-of-bounds access** throws an error:
|
|
135
|
+
|
|
136
|
+
```text
|
|
137
|
+
[] -> .at(0) # Error: List index out of bounds
|
|
138
|
+
["a"] -> .at(5) # Error: List index out of bounds
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Use `??` for safe access with default:
|
|
142
|
+
|
|
143
|
+
```rill
|
|
144
|
+
["a"] :> $list
|
|
145
|
+
$list[0] ?? "default" # "a"
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
See [Collections](07_collections.md) for iteration operators.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Dicts
|
|
153
|
+
|
|
154
|
+
Key-value mappings with string keys:
|
|
155
|
+
|
|
156
|
+
```rill
|
|
157
|
+
[name: "alice", age: 30] :> $person
|
|
158
|
+
$person.name # "alice"
|
|
159
|
+
$person.age # 30
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**Access patterns:**
|
|
163
|
+
- `.field` — Literal field access
|
|
164
|
+
- `.$key` — Variable as key
|
|
165
|
+
- `.($i + 1)` — Computed expression as key
|
|
166
|
+
- `.(a || b)` — Alternatives (try keys left-to-right)
|
|
167
|
+
- `.field ?? default` — Default value if missing
|
|
168
|
+
- `.?field` — Existence check (returns bool)
|
|
169
|
+
- `.?field&type` — Existence + type check
|
|
170
|
+
|
|
171
|
+
**Missing key access** throws an error. Use `??` for safe access:
|
|
172
|
+
|
|
173
|
+
```rill
|
|
174
|
+
[:] :> $d
|
|
175
|
+
$d.missing ?? "" # "" (safe default)
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Dict Methods
|
|
179
|
+
|
|
180
|
+
| Method | Description |
|
|
181
|
+
|--------|-------------|
|
|
182
|
+
| `.keys` | All keys as list |
|
|
183
|
+
| `.values` | All values as list |
|
|
184
|
+
| `.entries` | List of `[key, value]` pairs |
|
|
185
|
+
|
|
186
|
+
```rill
|
|
187
|
+
[name: "test", count: 42] -> .keys # ["count", "name"]
|
|
188
|
+
[name: "test", count: 42] -> .values # [42, "test"]
|
|
189
|
+
[a: 1, b: 2] -> .entries # [["a", 1], ["b", 2]]
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
**Reserved methods** (`keys`, `values`, `entries`) cannot be used as dict keys.
|
|
193
|
+
|
|
194
|
+
### Dict Closures
|
|
195
|
+
|
|
196
|
+
Closures in dicts have `$` late-bound to the containing dict. See [Closures](06_closures.md) for details.
|
|
197
|
+
|
|
198
|
+
```rill
|
|
199
|
+
[
|
|
200
|
+
name: "toolkit",
|
|
201
|
+
count: 3,
|
|
202
|
+
str: ||"{$.name}: {$.count} items"
|
|
203
|
+
] :> $obj
|
|
204
|
+
|
|
205
|
+
$obj.str # "toolkit: 3 items" (auto-invoked)
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Tuples
|
|
211
|
+
|
|
212
|
+
Tuples package values for explicit argument unpacking at closure invocation. Created with the `*` spread operator:
|
|
213
|
+
|
|
214
|
+
```rill
|
|
215
|
+
# From list (positional)
|
|
216
|
+
*[1, 2, 3] :> $t # tuple with positional values
|
|
217
|
+
|
|
218
|
+
# From dict (named)
|
|
219
|
+
*[x: 1, y: 2] :> $t # tuple with named values
|
|
220
|
+
|
|
221
|
+
# Via pipe target
|
|
222
|
+
[1, 2, 3] -> * :> $t # convert list to tuple
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Using Tuples at Invocation
|
|
226
|
+
|
|
227
|
+
```rill
|
|
228
|
+
|a, b, c| { "{$a}-{$b}-{$c}" } :> $fmt
|
|
229
|
+
|
|
230
|
+
# Positional unpacking
|
|
231
|
+
*[1, 2, 3] -> $fmt() # "1-2-3"
|
|
232
|
+
|
|
233
|
+
# Named unpacking (order doesn't matter)
|
|
234
|
+
*[c: 3, a: 1, b: 2] -> $fmt() # "1-2-3"
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Strict Validation
|
|
238
|
+
|
|
239
|
+
When invoking with tuples, missing required parameters error, and extra arguments error:
|
|
240
|
+
|
|
241
|
+
```rill
|
|
242
|
+
|x, y|($x + $y) :> $fn
|
|
243
|
+
*[1] -> $fn() # Error: missing argument 'y'
|
|
244
|
+
*[1, 2, 3] -> $fn() # Error: extra positional argument
|
|
245
|
+
*[x: 1, z: 3] -> $fn() # Error: unknown argument 'z'
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Parameter Defaults with Tuples
|
|
249
|
+
|
|
250
|
+
```rill
|
|
251
|
+
|x, y = 10, z = 20|($x + $y + $z) :> $fn
|
|
252
|
+
*[5] -> $fn() # 35 (5 + 10 + 20)
|
|
253
|
+
*[x: 5, z: 30] -> $fn() # 45 (5 + 10 + 30)
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### Auto-Unpacking with Parallel Spread
|
|
257
|
+
|
|
258
|
+
When a closure is invoked with a single tuple argument, the tuple auto-unpacks:
|
|
259
|
+
|
|
260
|
+
```rill
|
|
261
|
+
# List of tuples with multi-arg closure
|
|
262
|
+
[*[1,2], *[3,4]] -> map |x,y|($x * $y) # [2, 12]
|
|
263
|
+
|
|
264
|
+
# Named tuples work too
|
|
265
|
+
[*[x:1, y:2], *[x:3, y:4]] -> map |x,y|($x + $y) # [3, 7]
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
## Type Assertions
|
|
271
|
+
|
|
272
|
+
Use type assertions to validate values at runtime.
|
|
273
|
+
|
|
274
|
+
### Assert Type (`:type`)
|
|
275
|
+
|
|
276
|
+
Error if type doesn't match, returns value unchanged:
|
|
277
|
+
|
|
278
|
+
```rill
|
|
279
|
+
# Postfix form (binds tighter than method calls)
|
|
280
|
+
42:number # passes, returns 42
|
|
281
|
+
(1 + 2):number # passes, returns 3
|
|
282
|
+
42:number.str # "42" - assertion then method
|
|
283
|
+
|
|
284
|
+
# Pipe target form
|
|
285
|
+
"hello" -> :string # passes, returns "hello"
|
|
286
|
+
"hello" -> :number # ERROR: expected number, got string
|
|
287
|
+
$val -> :dict -> .keys # assert dict, then get keys
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### Check Type (`:?type`)
|
|
291
|
+
|
|
292
|
+
Returns boolean, no error:
|
|
293
|
+
|
|
294
|
+
```rill
|
|
295
|
+
# Postfix form
|
|
296
|
+
42:?number # true
|
|
297
|
+
"hello":?number # false
|
|
298
|
+
|
|
299
|
+
# Pipe target form
|
|
300
|
+
"hello" -> :?string # true
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Type checks work in conditionals:
|
|
304
|
+
|
|
305
|
+
```text
|
|
306
|
+
$val -> :?list ? process() ! skip() # branch on type
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
**Supported types:** `string`, `number`, `bool`, `closure`, `list`, `dict`, `tuple`
|
|
310
|
+
|
|
311
|
+
### In Pipe Chains
|
|
312
|
+
|
|
313
|
+
```rill
|
|
314
|
+
# Assert type and continue processing
|
|
315
|
+
[1, 2, 3] -> :list -> each { $ * 2 }
|
|
316
|
+
|
|
317
|
+
# Multiple assertions in chain
|
|
318
|
+
"test" -> :string -> .len -> :number # 4
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### Use Cases
|
|
322
|
+
|
|
323
|
+
```rill
|
|
324
|
+
# Validate function input
|
|
325
|
+
|data| {
|
|
326
|
+
$data -> :list # assert input is list
|
|
327
|
+
$data -> each { $ * 2 }
|
|
328
|
+
} :> $process_items
|
|
329
|
+
|
|
330
|
+
# Type-safe branching
|
|
331
|
+
|val| {
|
|
332
|
+
$val -> :?number ? ($val * 2) ! ($val -> .len)
|
|
333
|
+
} :> $process
|
|
334
|
+
$process(5) # 10
|
|
335
|
+
$process("hello") # 5
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
## Type-Locked Variables
|
|
341
|
+
|
|
342
|
+
Variables lock type on first assignment. The type is inferred from the value or declared explicitly:
|
|
343
|
+
|
|
344
|
+
```rill
|
|
345
|
+
"hello" :> $name # implicit: locked as string
|
|
346
|
+
"world" :> $name # OK: same type
|
|
347
|
+
5 :> $name # ERROR: cannot assign number to string
|
|
348
|
+
|
|
349
|
+
"hello" :> $name:string # explicit: declare and lock as string
|
|
350
|
+
42 :> $count:number # explicit: declare and lock as number
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### Inline Capture with Type
|
|
354
|
+
|
|
355
|
+
```rill
|
|
356
|
+
"hello" :> $x:string -> .len # type annotation in mid-chain
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
Type annotations validate on assignment and prevent accidental type changes:
|
|
360
|
+
|
|
361
|
+
```rill
|
|
362
|
+
|x|$x :> $fn # locked as closure
|
|
363
|
+
"text" :> $fn # ERROR: cannot assign string to closure
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
## Global Type Functions
|
|
369
|
+
|
|
370
|
+
| Function | Description |
|
|
371
|
+
|----------|-------------|
|
|
372
|
+
| `type` | Returns type name as string |
|
|
373
|
+
| `json` | Convert to JSON string |
|
|
374
|
+
|
|
375
|
+
```rill
|
|
376
|
+
42 -> type # "number"
|
|
377
|
+
"hello" -> type # "string"
|
|
378
|
+
[1, 2] -> type # "list"
|
|
379
|
+
*[1, 2] -> type # "tuple"
|
|
380
|
+
[a: 1] -> type # "dict"
|
|
381
|
+
||{ $ } -> type # "closure"
|
|
382
|
+
|
|
383
|
+
[a: 1, b: 2] -> json # '{"a":1,"b":2}'
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
**`json` closure handling:**
|
|
387
|
+
- Direct closure → error: `|x|{ $x } -> json` throws "Cannot serialize closure to JSON"
|
|
388
|
+
- Closures in dicts → skipped: `[a: 1, fn: ||{ 0 }] -> json` returns `'{"a":1}'`
|
|
389
|
+
- Closures in lists → skipped: `[1, ||{ 0 }, 2] -> json` returns `'[1,2]'`
|
|
390
|
+
|
|
391
|
+
---
|
|
392
|
+
|
|
393
|
+
## See Also
|
|
394
|
+
|
|
395
|
+
- [Variables](03_variables.md) — Declaration, scope, `$` binding
|
|
396
|
+
- [Closures](06_closures.md) — Closure semantics and patterns
|
|
397
|
+
- [Collections](07_collections.md) — List iteration operators
|
|
398
|
+
- [Strings](09_strings.md) — String methods reference
|
|
399
|
+
- [Reference](11_reference.md) — Quick reference tables
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
# rill Variables and Scope
|
|
2
|
+
|
|
3
|
+
*Variable declaration, type locking, and scope rules*
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
rill uses capture (`:>`) instead of assignment. Variables are type-locked after first assignment and follow strict scoping rules.
|
|
8
|
+
|
|
9
|
+
**Key principles:**
|
|
10
|
+
- **Capture, not assign**: Use `:>` to capture values into variables
|
|
11
|
+
- **Type-locked**: Variables lock type on first assignment
|
|
12
|
+
- **No shadowing**: Cannot redeclare a variable name from outer scope
|
|
13
|
+
- **No leakage**: Variables created inside blocks don't exist outside
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Variable Declaration
|
|
18
|
+
|
|
19
|
+
Variables are declared via capture (`:>`), not assignment:
|
|
20
|
+
|
|
21
|
+
```rill
|
|
22
|
+
"hello" :> $greeting
|
|
23
|
+
42 :> $count
|
|
24
|
+
[1, 2, 3] :> $items
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Capture and Continue
|
|
28
|
+
|
|
29
|
+
The `:>` operator captures the value AND continues the chain:
|
|
30
|
+
|
|
31
|
+
```rill
|
|
32
|
+
"hello"
|
|
33
|
+
:> $greeting # capture "hello" into $greeting
|
|
34
|
+
-> "{$} world" # $ is still "hello"
|
|
35
|
+
:> $message # capture "hello world" into $message
|
|
36
|
+
-> .upper # result: "HELLO WORLD"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Terminal Capture
|
|
40
|
+
|
|
41
|
+
Capture at end of expression stores and ends the chain:
|
|
42
|
+
|
|
43
|
+
```rill
|
|
44
|
+
"hello" :> $result # capture and end chain (result: "hello")
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## The Pipe Variable `$`
|
|
50
|
+
|
|
51
|
+
`$` holds the current piped value in the current scope:
|
|
52
|
+
|
|
53
|
+
```rill
|
|
54
|
+
"test value" -> {
|
|
55
|
+
.upper -> log # $ is "test value", logs "TEST VALUE"
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### `$` Binding by Context
|
|
60
|
+
|
|
61
|
+
| Context | `$` contains |
|
|
62
|
+
|---------|--------------|
|
|
63
|
+
| Inline block `-> { }` | Piped value |
|
|
64
|
+
| Each loop `-> each { }` | Current iteration item |
|
|
65
|
+
| While-loop `(cond) @ { }` | Accumulated value |
|
|
66
|
+
| Do-while `@ { } ? cond` | Accumulated value |
|
|
67
|
+
| Conditional `cond ? { }` | Tested value |
|
|
68
|
+
| Piped conditional `-> ? { }` | Piped value (also used as condition) |
|
|
69
|
+
| Stored closure `\|x\|{ }` | N/A — use explicit params |
|
|
70
|
+
| Dict closure `\|\|{ $.x }` | Dict self (`this`) — late-bound |
|
|
71
|
+
|
|
72
|
+
### Implied `$`
|
|
73
|
+
|
|
74
|
+
When certain constructs appear without explicit input, `$` is used implicitly:
|
|
75
|
+
|
|
76
|
+
| Written | Equivalent to | Context |
|
|
77
|
+
|---------|---------------|---------|
|
|
78
|
+
| `? { }` | `$ -> ? { }` | Piped conditional ($ as condition) |
|
|
79
|
+
| `.method()` | `$ -> .method()` | Method call without receiver |
|
|
80
|
+
| `$fn()` | `$fn($)` | Closure call with no explicit args* |
|
|
81
|
+
|
|
82
|
+
*Closure calls receive `$` only when: no explicit args, first param has no default, and `$` is not a closure.
|
|
83
|
+
|
|
84
|
+
```rill
|
|
85
|
+
# Inside blocks, $ flows naturally
|
|
86
|
+
"test value" -> {
|
|
87
|
+
.upper -> log # $ is "test value"
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
# In each loops, $ is the current item
|
|
91
|
+
|x| { $x * 2 } :> $double
|
|
92
|
+
[1, 2, 3] -> each { $double() } # $double receives 1, 2, 3
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**When implied `$` does NOT apply:**
|
|
96
|
+
|
|
97
|
+
```rill
|
|
98
|
+
# Explicit args override implied $
|
|
99
|
+
|x| { $x } :> $fn
|
|
100
|
+
$fn("explicit") # uses "explicit", not $
|
|
101
|
+
|
|
102
|
+
# Params with defaults use the default
|
|
103
|
+
|x: string = "default"| { $x } :> $fn2
|
|
104
|
+
$fn2() # uses "default", not $
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Type-Locked Variables
|
|
110
|
+
|
|
111
|
+
Variables lock type after first assignment:
|
|
112
|
+
|
|
113
|
+
```rill
|
|
114
|
+
"hello" :> $name # locked as string
|
|
115
|
+
"world" :> $name # OK: same type
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
```text
|
|
119
|
+
5 :> $name # ERROR: cannot assign number to string
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Explicit Type Annotations
|
|
123
|
+
|
|
124
|
+
Declare type explicitly with `:type`:
|
|
125
|
+
|
|
126
|
+
```rill
|
|
127
|
+
"hello" :> $name:string # declare and lock as string
|
|
128
|
+
42 :> $count:number # declare and lock as number
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**Supported types:** `string`, `number`, `bool`, `closure`, `list`, `dict`, `tuple`
|
|
132
|
+
|
|
133
|
+
### Inline Capture with Type
|
|
134
|
+
|
|
135
|
+
```rill
|
|
136
|
+
"hello" :> $x:string -> .len # type annotation in mid-chain
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Type annotations validate on assignment and prevent accidental type changes:
|
|
140
|
+
|
|
141
|
+
```rill
|
|
142
|
+
|x|$x :> $fn:closure # locked as closure
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
```text
|
|
146
|
+
"text" :> $fn # ERROR: cannot assign string to closure
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Scope Rules
|
|
152
|
+
|
|
153
|
+
Blocks, loops, conditionals, and grouped expressions create child scopes.
|
|
154
|
+
|
|
155
|
+
### Three Rules
|
|
156
|
+
|
|
157
|
+
1. **Read from parent:** Variables from outer scopes are accessible (read-only)
|
|
158
|
+
2. **No shadowing:** Cannot assign to a variable name that exists in an outer scope
|
|
159
|
+
3. **No leakage:** Variables created inside don't exist outside
|
|
160
|
+
|
|
161
|
+
```rill
|
|
162
|
+
"context" :> $ctx
|
|
163
|
+
|
|
164
|
+
"check" -> .contains("c") ? {
|
|
165
|
+
"process with {$ctx}" -> log # OK: read outer variable
|
|
166
|
+
"local" :> $temp # OK: new local variable
|
|
167
|
+
}
|
|
168
|
+
# $temp not accessible here
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
```text
|
|
172
|
+
"context" :> $ctx
|
|
173
|
+
"check" -> .contains("c") ? {
|
|
174
|
+
"new" :> $ctx # ERROR: cannot shadow outer $ctx
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### While Loops and `$`
|
|
179
|
+
|
|
180
|
+
While loops use `$` as the accumulator since named variables in the body don't persist across iterations:
|
|
181
|
+
|
|
182
|
+
```rill
|
|
183
|
+
# Use $ as accumulator (body result becomes next iteration's $)
|
|
184
|
+
0 -> ($ < 5) @ { $ + 1 } # Result: 5
|
|
185
|
+
|
|
186
|
+
# Variables inside loop body are local to each iteration
|
|
187
|
+
0 -> ($ < 3) @ {
|
|
188
|
+
($ * 10) :> $temp # $temp exists only in this iteration
|
|
189
|
+
$ + 1
|
|
190
|
+
}
|
|
191
|
+
# $temp not accessible here
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Reading Outer Variables
|
|
195
|
+
|
|
196
|
+
```rill
|
|
197
|
+
10 :> $x
|
|
198
|
+
[1, 2, 3] -> each {
|
|
199
|
+
$x + $ # Reads outer $x = 10
|
|
200
|
+
}
|
|
201
|
+
# Result: [11, 12, 13]
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## Special Variables
|
|
207
|
+
|
|
208
|
+
| Variable | Contains | Source |
|
|
209
|
+
|----------|----------|--------|
|
|
210
|
+
| `$` | Piped value (current block scope) | Grammar |
|
|
211
|
+
| `$ARGS` | CLI positional args (list) | Runtime |
|
|
212
|
+
| `$ENV.NAME` | Environment variable | Runtime |
|
|
213
|
+
| `$name` | Named variable | Runtime |
|
|
214
|
+
|
|
215
|
+
`$` is a grammar-level construct. All other variables are runtime-provided with the same scoping rules as user-defined variables.
|
|
216
|
+
|
|
217
|
+
### `$ARGS`
|
|
218
|
+
|
|
219
|
+
Access CLI positional arguments:
|
|
220
|
+
|
|
221
|
+
```text
|
|
222
|
+
$ARGS[0] # first argument
|
|
223
|
+
$ARGS[1] # second argument
|
|
224
|
+
$ARGS -> each { log($) } # iterate all arguments
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### `$ENV`
|
|
228
|
+
|
|
229
|
+
Access environment variables:
|
|
230
|
+
|
|
231
|
+
```text
|
|
232
|
+
$ENV.HOME # /home/user
|
|
233
|
+
$ENV.PATH # /usr/bin:...
|
|
234
|
+
$ENV.DEPLOY_ENV ?? "dev" # with default
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Runtime-Provided Variables
|
|
238
|
+
|
|
239
|
+
Named variables like `$file` or `$config` are provided by the host runtime. rill treats them as any other variable in the outer scope:
|
|
240
|
+
|
|
241
|
+
```text
|
|
242
|
+
---
|
|
243
|
+
args: file: string, retries: number = 3
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
# $file and $retries available because host parsed frontmatter
|
|
247
|
+
process($file, $retries)
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## Inline Capture Pattern
|
|
253
|
+
|
|
254
|
+
Captures can appear mid-chain for debugging or later reference. Semantically, `:> $a ->` stores the value and returns it unchanged (like `log`):
|
|
255
|
+
|
|
256
|
+
```rill
|
|
257
|
+
"analyze this" :> $result -> .upper -> .len
|
|
258
|
+
# $result is "analyze this", final result is 12
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
The value flows: `"analyze this"` → stored in `$result` → uppercased → length.
|
|
262
|
+
|
|
263
|
+
### Debugging Pattern
|
|
264
|
+
|
|
265
|
+
```rill
|
|
266
|
+
"test" :> $input -> log -> .upper :> $output -> log
|
|
267
|
+
# logs "test", then logs "TEST"
|
|
268
|
+
# $input is "test", $output is "TEST"
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## Common Patterns
|
|
274
|
+
|
|
275
|
+
### Capture for Reuse
|
|
276
|
+
|
|
277
|
+
Capture when you need the value in multiple places:
|
|
278
|
+
|
|
279
|
+
```rill
|
|
280
|
+
"hello" :> $greeting
|
|
281
|
+
"{$greeting} world" :> $message
|
|
282
|
+
"{$greeting} there" :> $alt
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### Let Data Flow
|
|
286
|
+
|
|
287
|
+
Prefer implied `$` when the value flows directly to the next statement:
|
|
288
|
+
|
|
289
|
+
```text
|
|
290
|
+
# Verbose — unnecessary capture
|
|
291
|
+
app::prompt("check status") :> $status
|
|
292
|
+
$status -> .empty ? app::error("No status")
|
|
293
|
+
|
|
294
|
+
# Idiomatic — data flows naturally
|
|
295
|
+
app::prompt("check status")
|
|
296
|
+
.empty ? app::error("No status")
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### Accumulation in Loops
|
|
300
|
+
|
|
301
|
+
Use `$` for accumulation in while loops:
|
|
302
|
+
|
|
303
|
+
```rill
|
|
304
|
+
"" -> (.len < 5) @ { "{$}x" } # "xxxxx"
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
## See Also
|
|
310
|
+
|
|
311
|
+
- [Types](02_types.md) — Type system and type assertions
|
|
312
|
+
- [Control Flow](05_control-flow.md) — Conditionals and loops
|
|
313
|
+
- [Closures](06_closures.md) — Closure scope and late binding
|
|
314
|
+
- [Reference](11_reference.md) — Quick reference tables
|