@rcrsr/rill 0.5.0 → 0.6.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.
Files changed (240) hide show
  1. package/dist/generated/introspection-data.d.ts +1 -1
  2. package/dist/generated/introspection-data.d.ts.map +1 -1
  3. package/dist/generated/introspection-data.js +107 -186
  4. package/dist/generated/introspection-data.js.map +1 -1
  5. package/dist/generated/version-data.d.ts +1 -1
  6. package/dist/generated/version-data.js +3 -3
  7. package/dist/generated/version-data.js.map +1 -1
  8. package/dist/highlight-map.d.ts +4 -0
  9. package/dist/highlight-map.d.ts.map +1 -0
  10. package/dist/highlight-map.js +71 -0
  11. package/dist/highlight-map.js.map +1 -0
  12. package/dist/index.d.ts +2 -1
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +5 -1
  15. package/dist/index.js.map +1 -1
  16. package/dist/lexer/errors.d.ts.map +1 -1
  17. package/dist/lexer/errors.js +9 -3
  18. package/dist/lexer/errors.js.map +1 -1
  19. package/dist/lexer/operators.js +1 -1
  20. package/dist/lexer/tokenizer.d.ts.map +1 -1
  21. package/dist/lexer/tokenizer.js +0 -14
  22. package/dist/lexer/tokenizer.js.map +1 -1
  23. package/dist/parser/helpers.d.ts +8 -0
  24. package/dist/parser/helpers.d.ts.map +1 -1
  25. package/dist/parser/helpers.js +4 -4
  26. package/dist/parser/helpers.js.map +1 -1
  27. package/dist/parser/index.d.ts.map +1 -1
  28. package/dist/parser/index.js +1 -1
  29. package/dist/parser/index.js.map +1 -1
  30. package/dist/parser/parser-collect.js +1 -1
  31. package/dist/parser/parser-collect.js.map +1 -1
  32. package/dist/parser/parser-control.js +4 -4
  33. package/dist/parser/parser-control.js.map +1 -1
  34. package/dist/parser/parser-expr.js +32 -10
  35. package/dist/parser/parser-expr.js.map +1 -1
  36. package/dist/parser/parser-extract.js +7 -3
  37. package/dist/parser/parser-extract.js.map +1 -1
  38. package/dist/parser/parser-functions.d.ts.map +1 -1
  39. package/dist/parser/parser-functions.js +7 -18
  40. package/dist/parser/parser-functions.js.map +1 -1
  41. package/dist/parser/parser-literals.js +15 -15
  42. package/dist/parser/parser-literals.js.map +1 -1
  43. package/dist/parser/parser-script.js +3 -3
  44. package/dist/parser/parser-script.js.map +1 -1
  45. package/dist/parser/parser-variables.js +4 -4
  46. package/dist/parser/parser-variables.js.map +1 -1
  47. package/dist/parser/state.d.ts +1 -1
  48. package/dist/parser/state.d.ts.map +1 -1
  49. package/dist/parser/state.js +2 -2
  50. package/dist/parser/state.js.map +1 -1
  51. package/dist/runtime/core/callable.d.ts +20 -0
  52. package/dist/runtime/core/callable.d.ts.map +1 -1
  53. package/dist/runtime/core/callable.js +30 -7
  54. package/dist/runtime/core/callable.js.map +1 -1
  55. package/dist/runtime/core/context.d.ts +21 -0
  56. package/dist/runtime/core/context.d.ts.map +1 -1
  57. package/dist/runtime/core/context.js +75 -4
  58. package/dist/runtime/core/context.js.map +1 -1
  59. package/dist/runtime/core/eval/base.d.ts.map +1 -1
  60. package/dist/runtime/core/eval/base.js +3 -3
  61. package/dist/runtime/core/eval/base.js.map +1 -1
  62. package/dist/runtime/core/eval/index.d.ts.map +1 -1
  63. package/dist/runtime/core/eval/index.js +2 -0
  64. package/dist/runtime/core/eval/index.js.map +1 -1
  65. package/dist/runtime/core/eval/mixins/annotations.js +3 -3
  66. package/dist/runtime/core/eval/mixins/annotations.js.map +1 -1
  67. package/dist/runtime/core/eval/mixins/closures.d.ts.map +1 -1
  68. package/dist/runtime/core/eval/mixins/closures.js +69 -40
  69. package/dist/runtime/core/eval/mixins/closures.js.map +1 -1
  70. package/dist/runtime/core/eval/mixins/collections.js +15 -15
  71. package/dist/runtime/core/eval/mixins/collections.js.map +1 -1
  72. package/dist/runtime/core/eval/mixins/control-flow.d.ts.map +1 -1
  73. package/dist/runtime/core/eval/mixins/control-flow.js +12 -12
  74. package/dist/runtime/core/eval/mixins/control-flow.js.map +1 -1
  75. package/dist/runtime/core/eval/mixins/core.d.ts.map +1 -1
  76. package/dist/runtime/core/eval/mixins/core.js +12 -13
  77. package/dist/runtime/core/eval/mixins/core.js.map +1 -1
  78. package/dist/runtime/core/eval/mixins/expressions.js +9 -9
  79. package/dist/runtime/core/eval/mixins/expressions.js.map +1 -1
  80. package/dist/runtime/core/eval/mixins/extraction.d.ts.map +1 -1
  81. package/dist/runtime/core/eval/mixins/extraction.js +15 -15
  82. package/dist/runtime/core/eval/mixins/extraction.js.map +1 -1
  83. package/dist/runtime/core/eval/mixins/literals.js +22 -22
  84. package/dist/runtime/core/eval/mixins/literals.js.map +1 -1
  85. package/dist/runtime/core/eval/mixins/types.js +4 -4
  86. package/dist/runtime/core/eval/mixins/types.js.map +1 -1
  87. package/dist/runtime/core/eval/mixins/variables.js +34 -34
  88. package/dist/runtime/core/eval/mixins/variables.js.map +1 -1
  89. package/dist/runtime/core/execute.js +3 -3
  90. package/dist/runtime/core/execute.js.map +1 -1
  91. package/dist/runtime/core/introspection.d.ts +30 -1
  92. package/dist/runtime/core/introspection.d.ts.map +1 -1
  93. package/dist/runtime/core/introspection.js +47 -1
  94. package/dist/runtime/core/introspection.js.map +1 -1
  95. package/dist/runtime/core/types.d.ts +11 -0
  96. package/dist/runtime/core/types.d.ts.map +1 -1
  97. package/dist/runtime/core/types.js.map +1 -1
  98. package/dist/runtime/ext/builtins.js +22 -22
  99. package/dist/runtime/ext/builtins.js.map +1 -1
  100. package/dist/runtime/ext/extensions.d.ts +1 -1
  101. package/dist/runtime/ext/extensions.d.ts.map +1 -1
  102. package/dist/runtime/ext/extensions.js +4 -5
  103. package/dist/runtime/ext/extensions.js.map +1 -1
  104. package/dist/runtime/index.d.ts +6 -4
  105. package/dist/runtime/index.d.ts.map +1 -1
  106. package/dist/runtime/index.js +3 -2
  107. package/dist/runtime/index.js.map +1 -1
  108. package/dist/types.d.ts +36 -37
  109. package/dist/types.d.ts.map +1 -1
  110. package/dist/types.js +440 -120
  111. package/dist/types.js.map +1 -1
  112. package/package.json +7 -66
  113. package/README.md +0 -223
  114. package/dist/check/config.d.ts +0 -20
  115. package/dist/check/config.d.ts.map +0 -1
  116. package/dist/check/config.js +0 -151
  117. package/dist/check/config.js.map +0 -1
  118. package/dist/check/fixer.d.ts +0 -39
  119. package/dist/check/fixer.d.ts.map +0 -1
  120. package/dist/check/fixer.js +0 -119
  121. package/dist/check/fixer.js.map +0 -1
  122. package/dist/check/index.d.ts +0 -10
  123. package/dist/check/index.d.ts.map +0 -1
  124. package/dist/check/index.js +0 -21
  125. package/dist/check/index.js.map +0 -1
  126. package/dist/check/rules/anti-patterns.d.ts +0 -65
  127. package/dist/check/rules/anti-patterns.d.ts.map +0 -1
  128. package/dist/check/rules/anti-patterns.js +0 -481
  129. package/dist/check/rules/anti-patterns.js.map +0 -1
  130. package/dist/check/rules/closures.d.ts +0 -66
  131. package/dist/check/rules/closures.d.ts.map +0 -1
  132. package/dist/check/rules/closures.js +0 -370
  133. package/dist/check/rules/closures.js.map +0 -1
  134. package/dist/check/rules/collections.d.ts +0 -90
  135. package/dist/check/rules/collections.d.ts.map +0 -1
  136. package/dist/check/rules/collections.js +0 -373
  137. package/dist/check/rules/collections.js.map +0 -1
  138. package/dist/check/rules/conditionals.d.ts +0 -41
  139. package/dist/check/rules/conditionals.d.ts.map +0 -1
  140. package/dist/check/rules/conditionals.js +0 -134
  141. package/dist/check/rules/conditionals.js.map +0 -1
  142. package/dist/check/rules/flow.d.ts +0 -46
  143. package/dist/check/rules/flow.d.ts.map +0 -1
  144. package/dist/check/rules/flow.js +0 -206
  145. package/dist/check/rules/flow.js.map +0 -1
  146. package/dist/check/rules/formatting.d.ts +0 -133
  147. package/dist/check/rules/formatting.d.ts.map +0 -1
  148. package/dist/check/rules/formatting.js +0 -648
  149. package/dist/check/rules/formatting.js.map +0 -1
  150. package/dist/check/rules/helpers.d.ts +0 -26
  151. package/dist/check/rules/helpers.d.ts.map +0 -1
  152. package/dist/check/rules/helpers.js +0 -66
  153. package/dist/check/rules/helpers.js.map +0 -1
  154. package/dist/check/rules/index.d.ts +0 -21
  155. package/dist/check/rules/index.d.ts.map +0 -1
  156. package/dist/check/rules/index.js +0 -78
  157. package/dist/check/rules/index.js.map +0 -1
  158. package/dist/check/rules/loops.d.ts +0 -77
  159. package/dist/check/rules/loops.d.ts.map +0 -1
  160. package/dist/check/rules/loops.js +0 -310
  161. package/dist/check/rules/loops.js.map +0 -1
  162. package/dist/check/rules/naming.d.ts +0 -21
  163. package/dist/check/rules/naming.d.ts.map +0 -1
  164. package/dist/check/rules/naming.js +0 -174
  165. package/dist/check/rules/naming.js.map +0 -1
  166. package/dist/check/rules/strings.d.ts +0 -28
  167. package/dist/check/rules/strings.d.ts.map +0 -1
  168. package/dist/check/rules/strings.js +0 -79
  169. package/dist/check/rules/strings.js.map +0 -1
  170. package/dist/check/rules/types.d.ts +0 -41
  171. package/dist/check/rules/types.d.ts.map +0 -1
  172. package/dist/check/rules/types.js +0 -167
  173. package/dist/check/rules/types.js.map +0 -1
  174. package/dist/check/types.d.ts +0 -112
  175. package/dist/check/types.d.ts.map +0 -1
  176. package/dist/check/types.js +0 -6
  177. package/dist/check/types.js.map +0 -1
  178. package/dist/check/validator.d.ts +0 -18
  179. package/dist/check/validator.d.ts.map +0 -1
  180. package/dist/check/validator.js +0 -110
  181. package/dist/check/validator.js.map +0 -1
  182. package/dist/check/visitor.d.ts +0 -33
  183. package/dist/check/visitor.d.ts.map +0 -1
  184. package/dist/check/visitor.js +0 -259
  185. package/dist/check/visitor.js.map +0 -1
  186. package/dist/cli-check.d.ts +0 -43
  187. package/dist/cli-check.d.ts.map +0 -1
  188. package/dist/cli-check.js +0 -368
  189. package/dist/cli-check.js.map +0 -1
  190. package/dist/cli-eval.d.ts +0 -15
  191. package/dist/cli-eval.d.ts.map +0 -1
  192. package/dist/cli-eval.js +0 -116
  193. package/dist/cli-eval.js.map +0 -1
  194. package/dist/cli-exec.d.ts +0 -49
  195. package/dist/cli-exec.d.ts.map +0 -1
  196. package/dist/cli-exec.js +0 -183
  197. package/dist/cli-exec.js.map +0 -1
  198. package/dist/cli-module-loader.d.ts +0 -19
  199. package/dist/cli-module-loader.d.ts.map +0 -1
  200. package/dist/cli-module-loader.js +0 -83
  201. package/dist/cli-module-loader.js.map +0 -1
  202. package/dist/cli-shared.d.ts +0 -44
  203. package/dist/cli-shared.d.ts.map +0 -1
  204. package/dist/cli-shared.js +0 -108
  205. package/dist/cli-shared.js.map +0 -1
  206. package/dist/cli.d.ts +0 -13
  207. package/dist/cli.d.ts.map +0 -1
  208. package/dist/cli.js +0 -62
  209. package/dist/cli.js.map +0 -1
  210. package/dist/runtime/core/introspection-data.d.ts +0 -2
  211. package/dist/runtime/core/introspection-data.d.ts.map +0 -1
  212. package/dist/runtime/core/introspection-data.js +0 -618
  213. package/dist/runtime/core/introspection-data.js.map +0 -1
  214. package/dist/runtime/core/version-data.d.ts +0 -18
  215. package/dist/runtime/core/version-data.d.ts.map +0 -1
  216. package/dist/runtime/core/version-data.js +0 -16
  217. package/dist/runtime/core/version-data.js.map +0 -1
  218. package/docs/00_INDEX.md +0 -67
  219. package/docs/01_guide.md +0 -390
  220. package/docs/02_types.md +0 -504
  221. package/docs/03_variables.md +0 -324
  222. package/docs/04_operators.md +0 -629
  223. package/docs/05_control-flow.md +0 -692
  224. package/docs/06_closures.md +0 -787
  225. package/docs/07_collections.md +0 -688
  226. package/docs/08_iterators.md +0 -330
  227. package/docs/09_strings.md +0 -205
  228. package/docs/10_parsing.md +0 -366
  229. package/docs/11_reference.md +0 -600
  230. package/docs/12_examples.md +0 -748
  231. package/docs/13_modules.md +0 -519
  232. package/docs/14_host-integration.md +0 -985
  233. package/docs/15_grammar.ebnf +0 -773
  234. package/docs/16_conventions.md +0 -695
  235. package/docs/17_cli-tools.md +0 -184
  236. package/docs/18_design-principles.md +0 -247
  237. package/docs/19_cookbook.md +0 -628
  238. package/docs/88_errors.md +0 -902
  239. package/docs/99_llm-reference.txt +0 -614
  240. package/docs/assets/logo.png +0 -0
@@ -1,787 +0,0 @@
1
- # Closures
2
-
3
- Closures are first-class values that capture their defining scope. This document covers closure semantics, binding behavior, and common patterns.
4
-
5
- ## Expression Delimiters
6
-
7
- rill has two expression delimiters with deterministic behavior:
8
-
9
- | Delimiter | Semantics | Produces |
10
- |-----------|-----------|----------|
11
- | `{ body }` | Deferred (closure creation) | `ScriptCallable` |
12
- | `( expr )` | Eager (immediate evaluation) | Result value |
13
-
14
- **Key distinction:**
15
- - **Parentheses `( )`** evaluate immediately and return the result
16
- - **Braces `{ }`** create a closure for later invocation (deferred execution)
17
-
18
- ### Pipe Target Exception
19
-
20
- When `{ }` appears as a pipe target, it creates a closure and **immediately invokes** it:
21
-
22
- ```rill
23
- 5 -> { $ + 1 } # 6 (same observable result as eager evaluation)
24
- ```
25
-
26
- This is conceptually two steps happening in sequence:
27
-
28
- ```text
29
- 5 -> { $ + 1 }
30
-
31
- Step 1: Create closure with implicit $ parameter
32
- Step 2: Invoke closure with piped value (5) as argument
33
-
34
- Result: 6
35
- ```
36
-
37
- The observable result matches eager evaluation, but the mechanism differs. This matters when understanding error messages or debugging—the closure exists momentarily before invocation.
38
-
39
- **Comparison:**
40
-
41
- | Expression | Mechanism | Result |
42
- |------------|-----------|--------|
43
- | `5 -> { $ + 1 }` | Create closure, invoke with 5 | 6 |
44
- | `5 -> ($ + 1)` | Evaluate expression with $ = 5 | 6 |
45
- | `{ $ + 1 } :> $fn` | Create closure, store it | closure |
46
- | `($ + 1) :> $x` | Error: $ undefined outside pipe | — |
47
-
48
- The last row shows the key difference: `( )` requires `$` to already be defined, while `{ }` captures `$` as a parameter for later binding.
49
-
50
- ## Closure Syntax
51
-
52
- ```text
53
- # Block-closure (implicit $ parameter)
54
- { body }
55
-
56
- # Zero-parameter closure (property-style, no parameters)
57
- || { body }
58
- ||body # shorthand for simple expressions
59
-
60
- # With parameters
61
- |x| { body }
62
- |x|body # shorthand
63
- |x, y| { body } # multiple parameters
64
- |x: string| { body } # typed parameter
65
- |x: number = 10| { body } # default value (type inferred)
66
- |x: string = "hi"| { body } # default with explicit type
67
- ```
68
-
69
- ## Block-Closures
70
-
71
- Block syntax `{ body }` creates a closure with an implicit `$` parameter. This enables deferred execution and reusable transformations.
72
-
73
- ### Basic Block-Closure
74
-
75
- ```rill
76
- { $ + 1 } :> $increment
77
-
78
- 5 -> $increment # 6
79
- 10 -> $increment # 11
80
- $increment(7) # 8 (direct call also works)
81
- ```
82
-
83
- The block `{ $ + 1 }` produces a closure. When invoked, `$` is bound to the argument.
84
-
85
- ### Block-Closures vs Zero-Param Closures
86
-
87
- | Form | Params | `isProperty` | Behavior |
88
- |------|--------|-------------|----------|
89
- | `{ body }` (block-closure) | `[{ name: "$" }]` | `false` | Requires argument |
90
- | `\|\|{ body }` (zero-param) | `[]` | `true` | Auto-invokes on dict access |
91
-
92
- ```rill
93
- { $ * 2 } :> $double
94
- || { 42 } :> $constant
95
-
96
- 5 -> $double # 10 (requires argument)
97
- $constant() # 42 (no argument needed)
98
-
99
- # In dicts
100
- [
101
- double: { $ * 2 },
102
- constant: || { 42 }
103
- ] :> $obj
104
-
105
- $obj.double(5) # 10 (explicit call required)
106
- $obj.constant # 42 (auto-invoked on access)
107
- ```
108
-
109
- Block-closures require an argument; zero-param closures do not.
110
-
111
- ### Type Checking
112
-
113
- Block-closures check type at runtime:
114
-
115
- ```rill
116
- { $ + 1 } :> $fn
117
-
118
- type($fn) # "closure"
119
- $fn(5) # 6
120
- $fn("text") # Error: Cannot add string and number
121
- ```
122
-
123
- ### Multi-Statement Block-Closures
124
-
125
- Block-closures can contain multiple statements:
126
-
127
- ```rill
128
- {
129
- ($ * 2) :> $doubled
130
- "{$}: doubled is {$doubled}"
131
- } :> $describe
132
-
133
- 5 -> $describe # "5: doubled is 10"
134
- ```
135
-
136
- ### Collection Operations
137
-
138
- Block-closures integrate with collection operators:
139
-
140
- ```rill
141
- [1, 2, 3] -> map { $ * 2 } # [2, 4, 6]
142
- [1, 2, 3] -> filter { $ > 1 } # [2, 3]
143
- [1, 2, 3] -> fold(0) { $@ + $ } # 6 ($@ is accumulator)
144
- ```
145
-
146
- ### Eager vs Deferred Evaluation
147
-
148
- The choice between `( )` and `{ }` determines when code executes:
149
-
150
- ```rill
151
- # Eager: parentheses evaluate immediately
152
- 5 -> ($ + 1) :> $result
153
- $result # 6 (number, already computed)
154
-
155
- # Deferred: braces create closure
156
- { $ + 1 } :> $addOne
157
- type($addOne) # "closure"
158
- 5 -> $addOne # 6
159
- 10 -> $addOne # 11 (invoked later with different value)
160
-
161
- # Practical difference
162
- (5 + 1) :> $six # 6 (immediate)
163
- { $ + 1 } :> $fn # closure (deferred)
164
-
165
- $six # 6
166
- 10 -> $fn # 11
167
- ```
168
-
169
- Use `( )` when you want the result now. Use `{ }` when you want reusable logic.
170
-
171
- ## Late Binding
172
-
173
- Closures resolve captured variables at **call time**, not definition time. This enables recursive patterns and forward references.
174
-
175
- ### Basic Example
176
-
177
- ```rill
178
- 10 :> $x
179
- ||($x + 5) :> $fn
180
- 20 :> $x
181
- $fn() # 25 (sees current $x=20, not $x=10 at definition)
182
- ```
183
-
184
- ### Recursive Closures
185
-
186
- ```rill
187
- |n| { ($n < 1) ? 1 ! ($n * $factorial($n - 1)) } :> $factorial
188
- $factorial(5) # 120
189
- ```
190
-
191
- The closure references `$factorial` before it exists. Late binding resolves `$factorial` when the closure executes.
192
-
193
- ### Mutual Recursion
194
-
195
- ```rill
196
- |n| { ($n == 0) ? true ! $odd($n - 1) } :> $even
197
- |n| { ($n == 0) ? false ! $even($n - 1) } :> $odd
198
- $even(4) # true
199
- ```
200
-
201
- ### Forward References
202
-
203
- ```rill
204
- [
205
- || { $helper(1) },
206
- || { $helper(2) }
207
- ] :> $handlers
208
-
209
- |n| { $n * 10 } :> $helper # defined after closures
210
-
211
- $handlers[0]() # 10
212
- $handlers[1]() # 20
213
- ```
214
-
215
- ### Variable Mutation Visibility
216
-
217
- Closures see the current value of captured variables:
218
-
219
- ```rill
220
- 0 :> $counter
221
- || { $counter } :> $get
222
- || { $counter + 1 } :> $getPlus1
223
-
224
- 5 :> $counter
225
-
226
- [$get(), $getPlus1()] # [5, 6]
227
- ```
228
-
229
- ## Dict-Bound Closures
230
-
231
- Closures stored in dicts have `$` late-bound to the containing dict at invocation (like `this` in other languages).
232
-
233
- ### Zero-Arg Closures Auto-Invoke
234
-
235
- ```rill
236
- [
237
- name: "toolkit",
238
- count: 3,
239
- summary: || { "{$.name}: {$.count} items" }
240
- ] :> $obj
241
-
242
- $obj.summary # "toolkit: 3 items" (auto-invoked on access)
243
- ```
244
-
245
- ### Accessing Sibling Fields
246
-
247
- ```rill
248
- [
249
- width: 10,
250
- height: 5,
251
- area: || { $.width * $.height }
252
- ] :> $rect
253
-
254
- $rect.area # 50
255
- ```
256
-
257
- ### Parameterized Dict Closures
258
-
259
- ```rill
260
- [
261
- name: "tools",
262
- greet: |x| { "{$.name} says: {$x}" }
263
- ] :> $obj
264
-
265
- $obj.greet("hello") # "tools says: hello"
266
- ```
267
-
268
- ### Reusable Closures Across Dicts
269
-
270
- ```rill
271
- || { "{$.name}: {$.count} items" } :> $describer
272
-
273
- [name: "tools", count: 3, str: $describer] :> $obj1
274
- [name: "actions", count: 5, str: $describer] :> $obj2
275
-
276
- $obj1.str # "tools: 3 items"
277
- $obj2.str # "actions: 5 items"
278
- ```
279
-
280
- ### Calling Sibling Methods
281
-
282
- ```rill
283
- [
284
- double: |n| { $n * 2 },
285
- quad: |n| { $.double($.double($n)) }
286
- ] :> $math
287
-
288
- $math.quad(3) # 12
289
- ```
290
-
291
- ## List-Stored Closures
292
-
293
- Closures in lists maintain their defining scope. Invoke via bracket access:
294
-
295
- ```rill
296
- [
297
- |x| { $x + 1 },
298
- |x| { $x * 2 },
299
- |x| { $x * $x }
300
- ] :> $transforms
301
-
302
- $transforms[0](5) # 6
303
- $transforms[1](5) # 10
304
- $transforms[2](5) # 25
305
- ```
306
-
307
- ### Chaining List Closures
308
-
309
- ```rill
310
- |n| { $n + 1 } :> $inc
311
- |n| { $n * 2 } :> $double
312
-
313
- 5 -> @[$inc, $double, $inc] # 13: (5+1)*2+1
314
- ```
315
-
316
- ## Inline Closures
317
-
318
- Closures can appear inline in expressions:
319
-
320
- ```rill
321
- [1, 2, 3] -> map |x| { $x * 2 } # [2, 4, 6]
322
-
323
- [1, 2, 3] -> filter |x| { $x > 1 } # [2, 3]
324
-
325
- [1, 2, 3] -> fold(0) |acc, x| { $acc + $x } # 6
326
- ```
327
-
328
- ### Inline with Block Bodies
329
-
330
- ```rill
331
- [1, 2, 3] -> map |x| {
332
- ($x * 10) :> $scaled
333
- "{$x} -> {$scaled}"
334
- }
335
- # ["1 -> 10", "2 -> 20", "3 -> 30"]
336
- ```
337
-
338
- ## Nested Closures
339
-
340
- Closures can contain closures. Each captures its defining scope:
341
-
342
- ```rill
343
- |n| { || { $n } } :> $makeGetter
344
- $makeGetter(42)() # 42
345
- ```
346
-
347
- ### Closure Factory Pattern
348
-
349
- ```rill
350
- |multiplier| {
351
- |x| { $x * $multiplier }
352
- } :> $makeMultiplier
353
-
354
- $makeMultiplier(3) :> $triple
355
- $makeMultiplier(10) :> $tenX
356
-
357
- $triple(5) # 15
358
- $tenX(5) # 50
359
- ```
360
-
361
- ### Nested Late Binding
362
-
363
- ```rill
364
- 1 :> $x
365
- || { || { $x } } :> $outer
366
- 5 :> $x
367
- $outer()() # 5 (inner closure sees updated $x)
368
- ```
369
-
370
- ## Parameter Shadowing
371
-
372
- Closure parameters shadow captured variables of the same name:
373
-
374
- ```rill
375
- 100 :> $x
376
- |x| { $x * 2 } :> $double
377
- $double(5) # 10 (parameter $x=5 shadows captured $x=100)
378
- ```
379
-
380
- ## Scope Isolation
381
-
382
- ### Loop Closures
383
-
384
- Each loop iteration creates a new child scope. Capture variables explicitly to preserve per-iteration values:
385
-
386
- ```rill
387
- # Capture $ into named variable for each iteration
388
- [1, 2, 3] -> each {
389
- $ :> $item
390
- || { $item }
391
- } :> $closures
392
-
393
- [$closures[0](), $closures[1](), $closures[2]()] # [1, 2, 3]
394
- ```
395
-
396
- **Note:** `$` (pipeValue) is a context property, not a variable. Use explicit capture for closure access.
397
-
398
- ### Conditional Branch Closures
399
-
400
- ```rill
401
- 10 :> $x
402
- true ? { || { $x } } ! { || { 0 } } :> $fn
403
- 20 :> $x
404
- $fn() # 20 (late binding sees updated $x)
405
- ```
406
-
407
- ## Invocation Patterns
408
-
409
- ### Direct Call
410
-
411
- ```rill
412
- |x| { $x + 1 } :> $inc
413
- $inc(5) # 6
414
-
415
- { $ + 1 } :> $inc
416
- $inc(5) # 6 (block-closure)
417
- ```
418
-
419
- ### Pipe Call
420
-
421
- ```rill
422
- |x| { $x + 1 } :> $inc
423
- 5 -> $inc() # 6
424
-
425
- { $ + 1 } :> $inc
426
- 5 -> $inc # 6 (block-closure, no parens needed)
427
- ```
428
-
429
- Block-closures work seamlessly with pipe syntax since `$` receives the piped value.
430
-
431
- ### Postfix Invocation
432
-
433
- Call closures from bracket access or expressions:
434
-
435
- ```rill
436
- [|x| { $x * 2 }] :> $fns
437
- $fns[0](5) # 10
438
-
439
- [{ $ * 2 }] :> $fns
440
- $fns[0](5) # 10 (block-closure)
441
-
442
- || { |n| { $n * 2 } } :> $factory
443
- $factory()(5) # 10 (chained invocation)
444
- ```
445
-
446
- ### Method Access After Bracket (Requires Grouping)
447
-
448
- ```rill
449
- ["hello", "world"] :> $list
450
-
451
- # Use grouping to call method on bracket result
452
- ($list[0]).upper # "HELLO"
453
-
454
- # Or use pipe syntax
455
- $list[0] -> .upper # "HELLO"
456
- ```
457
-
458
- Note: `$list[0].upper` parses `.upper` as field access on `$list`, not as a method call on the element. This throws an error since lists don't have an `upper` field.
459
-
460
- ## Parameter Metadata
461
-
462
- Closures expose parameter metadata via the `.params` property. This enables runtime introspection of function signatures.
463
-
464
- ### Basic Usage
465
-
466
- ```rill
467
- |x, y| { $x + $y } :> $add
468
- $add.params
469
- # [
470
- # x: [type: ""],
471
- # y: [type: ""]
472
- # ]
473
- ```
474
-
475
- ### Typed Parameters
476
-
477
- ```rill
478
- |name: string, age: number| { "{$name}: {$age}" } :> $format
479
- $format.params
480
- # [
481
- # name: [type: "string"],
482
- # age: [type: "number"]
483
- # ]
484
- ```
485
-
486
- ### Block-Closures
487
-
488
- Block-closures have an implicit `$` parameter:
489
-
490
- ```rill
491
- { $ * 2 } :> $double
492
- $double.params
493
- # [
494
- # $: [type: ""]
495
- # ]
496
- ```
497
-
498
- ### Zero-Parameter Closures
499
-
500
- ```rill
501
- || { 42 } :> $constant
502
- $constant.params
503
- # []
504
- ```
505
-
506
- ### Practical Use Cases
507
-
508
- **Generic Function Wrapper:**
509
-
510
- ```rill
511
- |fn| {
512
- $fn.params -> .keys -> .len :> $count
513
- "Function has {$count} parameter(s)"
514
- } :> $describe
515
-
516
- |x, y| { $x + $y } :> $add
517
- $describe($add) # "Function has 2 parameter(s)"
518
- ```
519
-
520
- **Validation:**
521
-
522
- ```text
523
- |fn| {
524
- $fn.params -> .entries -> each {
525
- $[1].type -> .empty ? "Missing type annotation: {$[0]}" ! ""
526
- } -> filter { !$ -> .empty }
527
- } :> $checkTypes
528
-
529
- |x, y: number| { $x + $y } :> $partial
530
- $checkTypes($partial) # ["Missing type annotation: x"]
531
- ```
532
-
533
- ## Parameter Annotations
534
-
535
- Parameters can have their own annotations using `^(key: value)` syntax after the parameter name. These attach metadata to individual parameters for validation, configuration, or documentation purposes.
536
-
537
- ### Syntax and Ordering
538
-
539
- Parameter annotations appear in a specific order:
540
-
541
- ```text
542
- |paramName: type ^(annotations) = default| body
543
- ```
544
-
545
- **Ordering rules:**
546
- 1. Parameter name (required)
547
- 2. Type annotation with `:` (optional)
548
- 3. Parameter annotations with `^()` (optional)
549
- 4. Default value with `=` (optional)
550
-
551
- ```rill
552
- |x: number ^(min: 0, max: 100)|($x) :> $validate
553
- |name: string ^(required: true) = "guest"|($name) :> $greet
554
- |count ^(cache: true) = 0|($count) :> $process
555
- ```
556
-
557
- ### Access Pattern
558
-
559
- Parameter annotations are accessed via `.params.paramName.__annotations.key`:
560
-
561
- ```rill
562
- |x: number ^(min: 0, max: 100), y: string|($x + $y) :> $fn
563
-
564
- $fn.params
565
- # Returns:
566
- # [
567
- # x: [type: "number", __annotations: [min: 0, max: 100]],
568
- # y: [type: "string"]
569
- # ]
570
-
571
- $fn.params.x.__annotations.min # 0
572
- $fn.params.x.__annotations.max # 100
573
- $fn.params.y.?__annotations # false (no annotations on y)
574
- ```
575
-
576
- ### Validation Metadata
577
-
578
- Use parameter annotations to specify constraints:
579
-
580
- ```rill
581
- |value: number ^(min: 0, max: 100)|($value) :> $bounded
582
-
583
- $bounded.params.value.__annotations.min # 0
584
- $bounded.params.value.__annotations.max # 100
585
- ```
586
-
587
- **Generic validator pattern:**
588
-
589
- ```text
590
- |fn, arg| {
591
- $fn.params -> .entries -> .head -> *<$name, $meta>
592
- $meta.?__annotations ? {
593
- ($arg < $meta.__annotations.min) ? "Value {$arg} below min {$meta.__annotations.min}" !
594
- ($arg > $meta.__annotations.max) ? "Value {$arg} above max {$meta.__annotations.max}" !
595
- ""
596
- } ! ""
597
- } :> $validate
598
-
599
- |x: number ^(min: 0, max: 10)|($x) :> $ranged
600
- $validate($ranged, 15) # "Value 15 above max 10"
601
- ```
602
-
603
- ### Caching Hints
604
-
605
- Mark parameters that should trigger caching behavior:
606
-
607
- ```rill
608
- |key: string ^(cache: true)|($key) :> $fetch
609
-
610
- $fetch.params.key.__annotations.cache # true
611
- ```
612
-
613
- ### Format Specifications
614
-
615
- Attach formatting metadata to parameters:
616
-
617
- ```rill
618
- |timestamp: string ^(format: "ISO8601")|($timestamp) :> $formatDate
619
-
620
- $formatDate.params.timestamp.__annotations.format # "ISO8601"
621
- ```
622
-
623
- ### Multiple Annotations
624
-
625
- Parameters can have multiple annotations:
626
-
627
- ```rill
628
- |email: string ^(required: true, pattern: ".*@.*", maxLength: 100)|($email) :> $validateEmail
629
-
630
- $validateEmail.params.email.__annotations.required # true
631
- $validateEmail.params.email.__annotations.pattern # ".*@.*"
632
- $validateEmail.params.email.__annotations.maxLength # 100
633
- ```
634
-
635
- ### Annotation-Driven Logic
636
-
637
- Use parameter annotations to drive runtime behavior:
638
-
639
- ```text
640
- |processor| {
641
- $processor.params -> .entries -> each {
642
- $[1].?__annotations ? {
643
- $[1].__annotations.?required ? "Parameter {$[0]} is required" ! ""
644
- } ! ""
645
- } -> filter { !$ -> .empty }
646
- } :> $getRequiredParams
647
-
648
- |x, y: string ^(required: true), z|($x) :> $fn
649
- $getRequiredParams($fn) # ["Parameter y is required"]
650
- ```
651
-
652
- ### Checking for Annotations
653
-
654
- Use existence check `.?__annotations` to determine if a parameter has annotations:
655
-
656
- ```rill
657
- |x: number ^(min: 0), y: string|($x + $y) :> $fn
658
-
659
- $fn.params.x.?__annotations # true
660
- $fn.params.y.?__annotations # false
661
- ```
662
-
663
- ## Annotation Reflection
664
-
665
- Closures support annotation reflection via `.^key` syntax. Annotations attach metadata to closures for runtime introspection.
666
-
667
- **Type Restriction:** Only closures support annotation reflection. Accessing `.^key` on primitives throws `RUNTIME_TYPE_ERROR`.
668
-
669
- ### Basic Annotation Access
670
-
671
- ```rill
672
- ^(min: 0, max: 100) |x|($x) :> $fn
673
-
674
- $fn.^min # 0
675
- $fn.^max # 100
676
- ```
677
-
678
- ### Complex Annotation Values
679
-
680
- Annotations can hold any value type:
681
-
682
- ```rill
683
- ^(config: [timeout: 30, endpoints: ["a", "b"]]) |x|($x) :> $fn
684
-
685
- $fn.^config.timeout # 30
686
- $fn.^config.endpoints[0] # "a"
687
- ```
688
-
689
- ### Default Value Coalescing
690
-
691
- Use the default value operator for optional annotations:
692
-
693
- ```rill
694
- |x|($x) :> $fn
695
- $fn.^timeout ?? 30 # 30 (uses default when annotation missing)
696
-
697
- ^(timeout: 60) |x|($x) :> $withTimeout
698
- $withTimeout.^timeout ?? 30 # 60 (uses annotated value)
699
- ```
700
-
701
- ### Annotation-Driven Logic
702
-
703
- ```rill
704
- ^(enabled: true) |x|($x) :> $processor
705
-
706
- $processor.^enabled ? "processing" ! "disabled" # "processing"
707
- ```
708
-
709
- ### Dynamic Annotations
710
-
711
- Annotation values are evaluated at closure creation:
712
-
713
- ```rill
714
- 10 :> $base
715
- ^(limit: $base * 10) |x|($x) :> $fn
716
- $fn.^limit # 100
717
- ```
718
-
719
- ### Error Cases
720
-
721
- **Undefined Annotation Key:**
722
-
723
- ```rill
724
- |x|($x) :> $fn
725
- $fn.^missing # Error: RUNTIME_UNDEFINED_ANNOTATION
726
- ```
727
-
728
- **Non-Closure Type:**
729
-
730
- ```text
731
- "hello" :> $str
732
- $str.^key # Error: RUNTIME_TYPE_ERROR
733
- ```
734
-
735
- All primitive types (string, number, boolean, list, dict) throw `RUNTIME_TYPE_ERROR` when accessing `.^key`.
736
-
737
- ## Error Behavior
738
-
739
- ### Undefined Variables
740
-
741
- Undefined variables throw an error at call time (rill has no null):
742
-
743
- ```text
744
- || { $undefined } :> $fn
745
- $fn() # Error: Undefined variable: $undefined
746
- ```
747
-
748
- ### Invoking Non-Callable
749
-
750
- ```rill
751
- [1, 2, 3] :> $list
752
- $list[0]() # Error: Cannot invoke non-callable value (got number)
753
- ```
754
-
755
- ### Type Errors
756
-
757
- ```rill
758
- |x: string| { $x } :> $fn
759
- $fn(42) # Error: Parameter type mismatch: x expects string, got number
760
- ```
761
-
762
- ## Implementation Notes
763
-
764
- ### Scope Chain
765
-
766
- Closures store a reference to their defining scope (`definingScope`). At invocation:
767
- 1. A child context is created with `definingScope` as parent
768
- 2. Parameters are bound in the child context
769
- 3. Variable lookups traverse: local → definingScope → parent chain
770
-
771
- ### Memory Considerations
772
-
773
- - Closures hold references to their defining scope
774
- - Scopes form a tree structure (no circular references)
775
- - Scopes remain live while referenced by closures
776
-
777
- ### Performance
778
-
779
- - Variable lookup traverses the scope chain at each access
780
- - No caching (ensures mutation visibility)
781
- - Closure creation is lightweight (stores reference, not copy)
782
-
783
- ## Related Documentation
784
-
785
- - [Reference](11_reference.md) — Language specification
786
- - [Collections](07_collections.md) — `each`, `map`, `filter`, `fold` with closures
787
- - [Guide](01_guide.md) — Getting started tutorial