@rcrsr/rill 0.2.4 → 0.4.2
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/README.md +58 -30
- package/dist/check/visitor.d.ts.map +1 -1
- package/dist/check/visitor.js +3 -0
- package/dist/check/visitor.js.map +1 -1
- package/dist/lexer/tokenizer.d.ts.map +1 -1
- package/dist/lexer/tokenizer.js +3 -0
- package/dist/lexer/tokenizer.js.map +1 -1
- package/dist/parser/helpers.d.ts.map +1 -1
- package/dist/parser/helpers.js +16 -0
- package/dist/parser/helpers.js.map +1 -1
- package/dist/parser/parser-expr.d.ts.map +1 -1
- package/dist/parser/parser-expr.js +48 -13
- package/dist/parser/parser-expr.js.map +1 -1
- package/dist/parser/parser-literals.d.ts +2 -1
- package/dist/parser/parser-literals.d.ts.map +1 -1
- package/dist/parser/parser-literals.js +63 -6
- package/dist/parser/parser-literals.js.map +1 -1
- package/dist/parser/parser-variables.js +5 -0
- package/dist/parser/parser-variables.js.map +1 -1
- package/dist/runtime/core/callable.d.ts +22 -4
- package/dist/runtime/core/callable.d.ts.map +1 -1
- package/dist/runtime/core/callable.js +45 -3
- package/dist/runtime/core/callable.js.map +1 -1
- package/dist/runtime/core/context.d.ts.map +1 -1
- package/dist/runtime/core/context.js +1 -0
- package/dist/runtime/core/context.js.map +1 -1
- package/dist/runtime/core/equals.d.ts.map +1 -1
- package/dist/runtime/core/equals.js +32 -4
- package/dist/runtime/core/equals.js.map +1 -1
- package/dist/runtime/core/eval/base.d.ts.map +1 -1
- package/dist/runtime/core/eval/base.js +4 -1
- package/dist/runtime/core/eval/base.js.map +1 -1
- package/dist/runtime/core/eval/mixins/closures.d.ts.map +1 -1
- package/dist/runtime/core/eval/mixins/closures.js +71 -0
- package/dist/runtime/core/eval/mixins/closures.js.map +1 -1
- package/dist/runtime/core/eval/mixins/core.d.ts.map +1 -1
- package/dist/runtime/core/eval/mixins/core.js +293 -8
- package/dist/runtime/core/eval/mixins/core.js.map +1 -1
- package/dist/runtime/core/eval/mixins/literals.d.ts +2 -0
- package/dist/runtime/core/eval/mixins/literals.d.ts.map +1 -1
- package/dist/runtime/core/eval/mixins/literals.js +446 -39
- package/dist/runtime/core/eval/mixins/literals.js.map +1 -1
- package/dist/runtime/core/eval/mixins/variables.d.ts.map +1 -1
- package/dist/runtime/core/eval/mixins/variables.js +42 -2
- package/dist/runtime/core/eval/mixins/variables.js.map +1 -1
- package/dist/types.d.ts +20 -6
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -1
- package/docs/00_INDEX.md +4 -4
- package/docs/02_types.md +77 -2
- package/docs/04_operators.md +57 -0
- package/docs/06_closures.md +438 -4
- package/docs/11_reference.md +214 -13
- package/docs/15_grammar.ebnf +55 -5
- package/docs/18_design-principles.md +37 -0
- package/docs/19_cookbook.md +628 -0
- package/docs/99_llm-reference.txt +268 -14
- package/package.json +1 -1
package/docs/06_closures.md
CHANGED
|
@@ -2,10 +2,58 @@
|
|
|
2
2
|
|
|
3
3
|
Closures are first-class values that capture their defining scope. This document covers closure semantics, binding behavior, and common patterns.
|
|
4
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
|
+
|
|
5
50
|
## Closure Syntax
|
|
6
51
|
|
|
7
52
|
```text
|
|
8
|
-
#
|
|
53
|
+
# Block-closure (implicit $ parameter)
|
|
54
|
+
{ body }
|
|
55
|
+
|
|
56
|
+
# Zero-parameter closure (property-style, no parameters)
|
|
9
57
|
|| { body }
|
|
10
58
|
||body # shorthand for simple expressions
|
|
11
59
|
|
|
@@ -18,9 +66,107 @@ Closures are first-class values that capture their defining scope. This document
|
|
|
18
66
|
|x: string = "hi"| { body } # default with explicit type
|
|
19
67
|
```
|
|
20
68
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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.
|
|
24
170
|
|
|
25
171
|
## Late Binding
|
|
26
172
|
|
|
@@ -265,6 +411,9 @@ $fn() # 20 (late binding sees updated $x)
|
|
|
265
411
|
```rill
|
|
266
412
|
|x| { $x + 1 } :> $inc
|
|
267
413
|
$inc(5) # 6
|
|
414
|
+
|
|
415
|
+
{ $ + 1 } :> $inc
|
|
416
|
+
$inc(5) # 6 (block-closure)
|
|
268
417
|
```
|
|
269
418
|
|
|
270
419
|
### Pipe Call
|
|
@@ -272,8 +421,13 @@ $inc(5) # 6
|
|
|
272
421
|
```rill
|
|
273
422
|
|x| { $x + 1 } :> $inc
|
|
274
423
|
5 -> $inc() # 6
|
|
424
|
+
|
|
425
|
+
{ $ + 1 } :> $inc
|
|
426
|
+
5 -> $inc # 6 (block-closure, no parens needed)
|
|
275
427
|
```
|
|
276
428
|
|
|
429
|
+
Block-closures work seamlessly with pipe syntax since `$` receives the piped value.
|
|
430
|
+
|
|
277
431
|
### Postfix Invocation
|
|
278
432
|
|
|
279
433
|
Call closures from bracket access or expressions:
|
|
@@ -282,6 +436,9 @@ Call closures from bracket access or expressions:
|
|
|
282
436
|
[|x| { $x * 2 }] :> $fns
|
|
283
437
|
$fns[0](5) # 10
|
|
284
438
|
|
|
439
|
+
[{ $ * 2 }] :> $fns
|
|
440
|
+
$fns[0](5) # 10 (block-closure)
|
|
441
|
+
|
|
285
442
|
|| { |n| { $n * 2 } } :> $factory
|
|
286
443
|
$factory()(5) # 10 (chained invocation)
|
|
287
444
|
```
|
|
@@ -300,6 +457,283 @@ $list[0] -> .upper # "HELLO"
|
|
|
300
457
|
|
|
301
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.
|
|
302
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
|
+
|
|
303
737
|
## Error Behavior
|
|
304
738
|
|
|
305
739
|
### Undefined Variables
|