@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.
Files changed (59) hide show
  1. package/README.md +58 -30
  2. package/dist/check/visitor.d.ts.map +1 -1
  3. package/dist/check/visitor.js +3 -0
  4. package/dist/check/visitor.js.map +1 -1
  5. package/dist/lexer/tokenizer.d.ts.map +1 -1
  6. package/dist/lexer/tokenizer.js +3 -0
  7. package/dist/lexer/tokenizer.js.map +1 -1
  8. package/dist/parser/helpers.d.ts.map +1 -1
  9. package/dist/parser/helpers.js +16 -0
  10. package/dist/parser/helpers.js.map +1 -1
  11. package/dist/parser/parser-expr.d.ts.map +1 -1
  12. package/dist/parser/parser-expr.js +48 -13
  13. package/dist/parser/parser-expr.js.map +1 -1
  14. package/dist/parser/parser-literals.d.ts +2 -1
  15. package/dist/parser/parser-literals.d.ts.map +1 -1
  16. package/dist/parser/parser-literals.js +63 -6
  17. package/dist/parser/parser-literals.js.map +1 -1
  18. package/dist/parser/parser-variables.js +5 -0
  19. package/dist/parser/parser-variables.js.map +1 -1
  20. package/dist/runtime/core/callable.d.ts +22 -4
  21. package/dist/runtime/core/callable.d.ts.map +1 -1
  22. package/dist/runtime/core/callable.js +45 -3
  23. package/dist/runtime/core/callable.js.map +1 -1
  24. package/dist/runtime/core/context.d.ts.map +1 -1
  25. package/dist/runtime/core/context.js +1 -0
  26. package/dist/runtime/core/context.js.map +1 -1
  27. package/dist/runtime/core/equals.d.ts.map +1 -1
  28. package/dist/runtime/core/equals.js +32 -4
  29. package/dist/runtime/core/equals.js.map +1 -1
  30. package/dist/runtime/core/eval/base.d.ts.map +1 -1
  31. package/dist/runtime/core/eval/base.js +4 -1
  32. package/dist/runtime/core/eval/base.js.map +1 -1
  33. package/dist/runtime/core/eval/mixins/closures.d.ts.map +1 -1
  34. package/dist/runtime/core/eval/mixins/closures.js +71 -0
  35. package/dist/runtime/core/eval/mixins/closures.js.map +1 -1
  36. package/dist/runtime/core/eval/mixins/core.d.ts.map +1 -1
  37. package/dist/runtime/core/eval/mixins/core.js +293 -8
  38. package/dist/runtime/core/eval/mixins/core.js.map +1 -1
  39. package/dist/runtime/core/eval/mixins/literals.d.ts +2 -0
  40. package/dist/runtime/core/eval/mixins/literals.d.ts.map +1 -1
  41. package/dist/runtime/core/eval/mixins/literals.js +446 -39
  42. package/dist/runtime/core/eval/mixins/literals.js.map +1 -1
  43. package/dist/runtime/core/eval/mixins/variables.d.ts.map +1 -1
  44. package/dist/runtime/core/eval/mixins/variables.js +42 -2
  45. package/dist/runtime/core/eval/mixins/variables.js.map +1 -1
  46. package/dist/types.d.ts +20 -6
  47. package/dist/types.d.ts.map +1 -1
  48. package/dist/types.js +3 -0
  49. package/dist/types.js.map +1 -1
  50. package/docs/00_INDEX.md +4 -4
  51. package/docs/02_types.md +77 -2
  52. package/docs/04_operators.md +57 -0
  53. package/docs/06_closures.md +438 -4
  54. package/docs/11_reference.md +214 -13
  55. package/docs/15_grammar.ebnf +55 -5
  56. package/docs/18_design-principles.md +37 -0
  57. package/docs/19_cookbook.md +628 -0
  58. package/docs/99_llm-reference.txt +268 -14
  59. package/package.json +1 -1
@@ -0,0 +1,628 @@
1
+ # rill Cookbook
2
+
3
+ *Advanced patterns for workflow orchestration*
4
+
5
+ This cookbook demonstrates idiomatic rill patterns for common programming tasks. Each recipe shows a complete, working example.
6
+
7
+ ## State Machines
8
+
9
+ State machines model systems with discrete states and transitions. Rill's dispatch operator makes state machines declarative and readable.
10
+
11
+ ### Basic State Machine
12
+
13
+ Use nested dict dispatch to look up transitions by state and event:
14
+
15
+ ```rill
16
+ # Traffic light state machine
17
+ [
18
+ green: [tick: "yellow", emergency: "red"],
19
+ yellow: [tick: "red", emergency: "red"],
20
+ red: [tick: "green", emergency: "red"]
21
+ ] :> $machine
22
+
23
+ # Current state and incoming event
24
+ "green" :> $state
25
+ "tick" :> $event
26
+
27
+ # Look up next state
28
+ $machine.$state.$event
29
+ # Result: "yellow"
30
+ ```
31
+
32
+ The pattern `$machine.$state.$event` chains two property accesses: first get the state's transition table, then get the event's target state.
33
+
34
+ ### State Machine with Actions
35
+
36
+ Embed closures in the transition table to execute side effects:
37
+
38
+ ```rill
39
+ # Order processing state machine
40
+ [
41
+ pending: [
42
+ pay: [next: "paid", action: ||{ log("Payment received") }],
43
+ cancel: [next: "cancelled", action: ||{ log("Order cancelled") }]
44
+ ],
45
+ paid: [
46
+ ship: [next: "shipped", action: ||{ log("Order shipped") }],
47
+ refund: [next: "refunded", action: ||{ log("Refund issued") }]
48
+ ],
49
+ shipped: [
50
+ deliver: [next: "delivered", action: ||{ log("Order delivered") }]
51
+ ]
52
+ ] :> $machine
53
+
54
+ "pending" :> $state
55
+ "pay" :> $event
56
+
57
+ # Get transition
58
+ $machine.$state.$event :> $transition
59
+
60
+ # Execute action and get next state
61
+ $transition.action()
62
+ $transition.next
63
+ # Result: "paid"
64
+ ```
65
+
66
+ ### State Machine Loop
67
+
68
+ Process a sequence of events through the machine:
69
+
70
+ ```rill
71
+ # Define machine
72
+ [
73
+ idle: [start: "running", stop: "idle"],
74
+ running: [pause: "paused", stop: "idle"],
75
+ paused: [resume: "running", stop: "idle"]
76
+ ] :> $machine
77
+
78
+ # Event sequence to process
79
+ ["start", "pause", "resume", "stop"] :> $events
80
+
81
+ # Process events, accumulating state history
82
+ $events -> fold("idle") {
83
+ $machine.($@).($) # $@ is accumulator (current state), $ is event
84
+ }
85
+ # Result: "idle"
86
+ ```
87
+
88
+ Track state history with `fold`:
89
+
90
+ ```rill
91
+ [
92
+ idle: [start: "running"],
93
+ running: [pause: "paused", stop: "idle"],
94
+ paused: [resume: "running"]
95
+ ] :> $machine
96
+
97
+ ["start", "pause", "resume"] :> $events
98
+
99
+ $events -> fold([current: "idle", history: []]) {
100
+ $ :> $event
101
+ $machine.($@.current) :> $stateConfig
102
+ $stateConfig -> .keys -> .has($event) ? {
103
+ $stateConfig.($event) :> $next
104
+ [current: $next, history: [...$@.history, $next]]
105
+ } ! $@
106
+ }
107
+ # Result: [current: "running", history: ["running", "paused", "running"]]
108
+ ```
109
+
110
+ ### Guard Conditions
111
+
112
+ Use closures as guards to conditionally allow transitions:
113
+
114
+ ```text
115
+ # Turnstile with coin counting (conceptual - dict spread not implemented)
116
+ [
117
+ locked: [
118
+ coin: [
119
+ guard: |ctx|($ctx.coins >= 1),
120
+ next: "unlocked",
121
+ update: |ctx|([...$ctx, coins: $ctx.coins - 1])
122
+ ]
123
+ ],
124
+ unlocked: [
125
+ push: [next: "locked"],
126
+ coin: [
127
+ next: "unlocked",
128
+ update: |ctx|([...$ctx, coins: $ctx.coins + 1])
129
+ ]
130
+ ]
131
+ ] :> $machine
132
+
133
+ # Initial context
134
+ [state: "locked", coins: 2] :> $ctx
135
+
136
+ # Process coin event
137
+ $machine.($ctx.state).coin :> $transition
138
+
139
+ # Check guard if present
140
+ $transition.?guard ? {
141
+ $transition.guard($ctx) ? {
142
+ # Guard passed - apply update and transition
143
+ $transition.?update
144
+ ? $transition.update($ctx)
145
+ ! $ctx
146
+ -> [...$, state: $transition.next]
147
+ } ! $ctx # Guard failed, no transition
148
+ } ! {
149
+ # No guard - always transition
150
+ $transition.?update
151
+ ? $transition.update($ctx)
152
+ ! $ctx
153
+ -> [...$, state: $transition.next]
154
+ }
155
+ # Result: [state: "unlocked", coins: 1]
156
+ ```
157
+
158
+ ### Hierarchical State Machine
159
+
160
+ Model nested states using path dispatch:
161
+
162
+ ```rill
163
+ # Media player with play/pause substates
164
+ [
165
+ stopped: [
166
+ play: "playing.normal"
167
+ ],
168
+ playing: [
169
+ normal: [
170
+ pause: "paused",
171
+ slow: "playing.slow",
172
+ fast: "playing.fast"
173
+ ],
174
+ slow: [
175
+ pause: "paused",
176
+ normal: "playing.normal"
177
+ ],
178
+ fast: [
179
+ pause: "paused",
180
+ normal: "playing.normal"
181
+ ]
182
+ ],
183
+ paused: [
184
+ play: "playing.normal",
185
+ stop: "stopped"
186
+ ]
187
+ ] :> $machine
188
+
189
+ # Parse hierarchical state
190
+ "playing.normal" :> $state
191
+ $state -> .split(".") :> $path
192
+
193
+ # Navigate to current state config
194
+ $path -> fold($machine) { $@.$ }
195
+ # Result: [pause: "paused", slow: "playing.slow", fast: "playing.fast"]
196
+ ```
197
+
198
+ ---
199
+
200
+ ## Dispatch Patterns
201
+
202
+ ### Computed Routing
203
+
204
+ Route values through different processors based on type or content:
205
+
206
+ ```text
207
+ # Conceptual - string keys with special chars require dispatch syntax
208
+ [
209
+ "application/json": |body|{ $body -> parse_json },
210
+ "text/plain": |body|{ $body -> .trim },
211
+ "text/csv": |body|{ $body -> .lines -> map { .split(",") } }
212
+ ] :> $parsers
213
+
214
+ "application/json" :> $contentType
215
+ "{\"name\": \"test\"}" :> $body
216
+
217
+ $contentType -> $parsers -> |parser|{ $parser($body) }
218
+ # Result: [name: "test"]
219
+ ```
220
+
221
+ ### Multi-Key Dispatch
222
+
223
+ Map multiple inputs to the same handler:
224
+
225
+ ```rill
226
+ # HTTP method routing - correct multi-key syntax
227
+ [
228
+ ["GET", "HEAD"]: [handler: "read", safe: true],
229
+ ["POST", "PUT", "PATCH"]: [handler: "write", safe: false],
230
+ ["DELETE"]: [handler: "delete", safe: false]
231
+ ] :> $routes
232
+
233
+ "POST" -> $routes
234
+ # Result: [handler: "write", safe: false]
235
+ ```
236
+
237
+ ### Default Handlers
238
+
239
+ Combine dispatch with `??` for fallback behavior:
240
+
241
+ ```rill
242
+ [
243
+ success: |r|{ "Completed: {$r.data}" },
244
+ error: |r|{ "Failed: {$r.message}" },
245
+ pending: |r|{ "Waiting..." }
246
+ ] :> $handlers
247
+
248
+ [status: "unknown", data: "test"] :> $response
249
+
250
+ $response.status -> $handlers ?? |r|{ "Unknown status: {$r.status}" }
251
+ -> |handler|{ $handler($response) }
252
+ # Result: "Unknown status: unknown"
253
+ ```
254
+
255
+ ---
256
+
257
+ ## Accumulator Patterns
258
+
259
+ ### Running Statistics
260
+
261
+ Calculate statistics in a single pass:
262
+
263
+ ```text
264
+ # Conceptual - dict spread [...$dict, key: val] not implemented
265
+ [23, 45, 12, 67, 34, 89, 56] :> $values
266
+
267
+ $values -> fold([sum: 0, count: 0, min: 999999, max: -999999]) {
268
+ [
269
+ sum: $@.sum + $,
270
+ count: $@.count + 1,
271
+ min: ($ < $@.min) ? $ ! $@.min,
272
+ max: ($ > $@.max) ? $ ! $@.max
273
+ ]
274
+ } :> $stats
275
+
276
+ [...$stats, avg: $stats.sum / $stats.count]
277
+ # Result: [sum: 326, count: 7, min: 12, max: 89, avg: 46.57...]
278
+ ```
279
+
280
+ ### Grouping
281
+
282
+ Group items by a computed key:
283
+
284
+ ```text
285
+ # Conceptual - uses dict spread and $$ syntax not implemented
286
+ [
287
+ [name: "Alice", dept: "Engineering"],
288
+ [name: "Bob", dept: "Sales"],
289
+ [name: "Carol", dept: "Engineering"],
290
+ [name: "Dave", dept: "Sales"]
291
+ ] :> $employees
292
+
293
+ $employees -> fold([]) {
294
+ $@.?($$.dept) ? {
295
+ # Key exists - append to list
296
+ [...$@, ($$.dept): [...$@.($$.dept), $$.name]]
297
+ } ! {
298
+ # New key - create list
299
+ [...$@, ($$.dept): [$$.name]]
300
+ }
301
+ }
302
+ # Result: [Engineering: ["Alice", "Carol"], Sales: ["Bob", "Dave"]]
303
+ ```
304
+
305
+ ### Deduplication
306
+
307
+ Remove duplicates while preserving order:
308
+
309
+ ```rill
310
+ ["a", "b", "a", "c", "b", "d", "a"] :> $items
311
+
312
+ $items -> fold([seen: [], result: []]) {
313
+ $@.seen -> .has($) ? $@ ! {
314
+ [
315
+ seen: [...$@.seen, $],
316
+ result: [...$@.result, $]
317
+ ]
318
+ }
319
+ } -> .result
320
+ # Result: ["a", "b", "c", "d"]
321
+ ```
322
+
323
+ ---
324
+
325
+ ## Control Flow Patterns
326
+
327
+ ### Early Exit with Validation
328
+
329
+ Validate multiple conditions and exit on first failure:
330
+
331
+ ```rill
332
+ [username: "", email: "test@", age: 15] :> $formData
333
+
334
+ $formData -> {
335
+ $.username -> .empty ? ([valid: false, err: "Username required"] -> return)
336
+ $.email -> .contains("@") -> ! ? ([valid: false, err: "Invalid email"] -> return)
337
+ ($.age < 18) ? ([valid: false, err: "Must be 18+"] -> return)
338
+ [valid: true, data: $]
339
+ }
340
+ # Result: [valid: false, err: "Username required"]
341
+ ```
342
+
343
+ ### Retry with Backoff
344
+
345
+ Retry an operation with exponential backoff:
346
+
347
+ ```rill
348
+ # Simulate flaky operation (host would provide real implementation)
349
+ |attempt|{
350
+ ($attempt < 3) ? [ok: false, err: "Network error"] ! [ok: true, data: "Success"]
351
+ } :> $operation
352
+
353
+ # Retry loop with backoff
354
+ 1 -> ($ <= 5) @ {
355
+ $operation($) :> $result
356
+ $result.ok ? ($result -> return)
357
+ log("Attempt {$} failed: {$result.err}")
358
+ $ + 1
359
+ } :> $final
360
+
361
+ $final.?ok ? $final ! [ok: false, err: "Max retries exceeded"]
362
+ # Result: [ok: true, data: "Success"]
363
+ ```
364
+
365
+ ### Pipeline with Short-Circuit
366
+
367
+ Process steps that can fail at any point:
368
+
369
+ ```rill
370
+ |input|{
371
+ [ok: true, value: $input -> .trim]
372
+ } :> $step1
373
+
374
+ |input|{
375
+ $input -> .len :> $len
376
+ ($len < 3) ? [ok: false, err: "Too short"] ! [ok: true, value: $input -> .upper]
377
+ } :> $step2
378
+
379
+ |input|{
380
+ $input -> .contains("HELLO") :> $hasHello
381
+ $hasHello ? [ok: true, value: $input] ! [ok: false, err: "Must contain HELLO"]
382
+ } :> $step3
383
+
384
+ # Chain steps with early exit
385
+ " test input " :> $pipelineInput
386
+ $pipelineInput -> {
387
+ $step1($) :> $r1
388
+ ($r1.ok == false) ? ($r1 -> return)
389
+
390
+ $step2($r1.value) :> $r2
391
+ ($r2.ok == false) ? ($r2 -> return)
392
+
393
+ $step3($r2.value) :> $r3
394
+ $r3
395
+ }
396
+ # Result: [ok: false, err: "Must contain HELLO"]
397
+ ```
398
+
399
+ ---
400
+
401
+ ## Data Transformation
402
+
403
+ ### Flatten Nested Structure
404
+
405
+ Flatten arbitrarily nested lists:
406
+
407
+ ```rill
408
+ # For known depth, chain operations
409
+ [[1, 2], [3, [4, 5]], [6]] :> $nested
410
+
411
+ # Flatten one level
412
+ $nested -> fold([]) { [...$@, ...$] }
413
+ # Result: [1, 2, 3, [4, 5], 6]
414
+ ```
415
+
416
+ ### Transpose Matrix
417
+
418
+ Convert rows to columns:
419
+
420
+ ```rill
421
+ [
422
+ [1, 2, 3],
423
+ [4, 5, 6],
424
+ [7, 8, 9]
425
+ ] :> $matrix
426
+
427
+ range(0, $matrix[0] -> .len) -> map |col|{
428
+ $matrix -> map |row|{ $row[$col] }
429
+ }
430
+ # Result: [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
431
+ ```
432
+
433
+ ### Zip Lists
434
+
435
+ Combine parallel lists into tuples:
436
+
437
+ ```rill
438
+ ["a", "b", "c"] :> $zipKeys
439
+ [1, 2, 3] :> $zipValues
440
+
441
+ range(0, $zipKeys -> .len) -> map |i|{
442
+ [$zipKeys[$i], $zipValues[$i]]
443
+ }
444
+ # Result: [["a", 1], ["b", 2], ["c", 3]]
445
+ ```
446
+
447
+ Converting to a dict requires dict spread (not yet implemented):
448
+
449
+ ```text
450
+ # Conceptual - dict spread [...$@, (key): val] not implemented
451
+ range(0, $zipKeys -> .len) -> fold([]) |i|{
452
+ [...$@, ($zipKeys[$i]): $zipValues[$i]]
453
+ }
454
+ # Result: [a: 1, b: 2, c: 3]
455
+ ```
456
+
457
+ ---
458
+
459
+ ## String Processing
460
+
461
+ ### Template Expansion
462
+
463
+ Simple template with variable substitution (using angle brackets as delimiters):
464
+
465
+ ```rill
466
+ "Hello <name>, your order <orderId> ships on <date>." :> $template
467
+
468
+ [name: "Alice", orderId: "12345", date: "2024-03-15"] :> $templateVars
469
+
470
+ $templateVars -> .entries -> fold($template) {
471
+ $@.replace_all("<{$[0]}>", $[1] -> .str)
472
+ }
473
+ # Result: "Hello Alice, your order 12345 ships on 2024-03-15."
474
+ ```
475
+
476
+ ### Parse Key-Value Pairs
477
+
478
+ Extract structured data from formatted text:
479
+
480
+ ```text
481
+ # Conceptual - uses dict spread with computed keys
482
+ "name=Alice;age=30;city=Seattle" :> $input
483
+
484
+ $input
485
+ -> .split(";")
486
+ -> fold([]) {
487
+ $ -> .split("=") -> *<$key, $value>
488
+ [...$@, ($key): $value]
489
+ }
490
+ # Result: [name: "Alice", age: "30", city: "Seattle"]
491
+ ```
492
+
493
+ ### Word Frequency
494
+
495
+ Count word occurrences:
496
+
497
+ ```text
498
+ # Conceptual - uses dynamic existence check and dict spread
499
+ "the quick brown fox jumps over the lazy dog the fox" :> $text
500
+
501
+ $text
502
+ -> .lower
503
+ -> .split(" ")
504
+ -> fold([]) {
505
+ $@.?$
506
+ ? [...$@, ($): $@.$ + 1]
507
+ ! [...$@, ($): 1]
508
+ }
509
+ # Result: [the: 3, quick: 1, brown: 1, fox: 2, jumps: 1, over: 1, lazy: 1, dog: 1]
510
+ ```
511
+
512
+ ---
513
+
514
+ ## Closure Patterns
515
+
516
+ ### Partial Application
517
+
518
+ Create specialized functions from general ones:
519
+
520
+ ```rill
521
+ # General formatter
522
+ |prefix, suffix, value|{
523
+ "{$prefix}{$value}{$suffix}"
524
+ } :> $format
525
+
526
+ # Partial application via closure
527
+ |value|{ $format("[", "]", $value) } :> $bracket
528
+ |value|{ $format("<", ">", $value) } :> $angle
529
+
530
+ $bracket("test") # "[test]"
531
+ $angle("html") # "<html>"
532
+ ```
533
+
534
+ ### Memoization Pattern
535
+
536
+ Cache expensive computations:
537
+
538
+ ```rill
539
+ # Build cache alongside computation
540
+ |n, cache|{
541
+ $cache.?($n -> .str) ? $cache.($n -> .str) ! {
542
+ # Compute fibonacci
543
+ ($n <= 1) ? $n ! {
544
+ $n - 1 -> |prev|{ |prev, cache|{ ... }($prev, $cache) } :> $a # Simplified
545
+ # Real memoization requires host support for mutable cache
546
+ $a + $n - 2
547
+ }
548
+ }
549
+ }
550
+ ```
551
+
552
+ Note: True memoization with persistent cache requires host-provided storage since rill values are immutable.
553
+
554
+ ### Composition
555
+
556
+ Combine functions into pipelines:
557
+
558
+ ```rill
559
+ |f, g|{
560
+ |x|{ $x -> $f() -> $g() }
561
+ } :> $compose
562
+
563
+ |x|{ $x * 2 } :> $double
564
+ |x|{ $x + 1 } :> $increment
565
+
566
+ $compose($double, $increment) :> $doubleThenIncrement
567
+
568
+ 5 -> $doubleThenIncrement()
569
+ # Result: 11
570
+ ```
571
+
572
+ ---
573
+
574
+ ## Validation Patterns
575
+
576
+ ### Schema Validation
577
+
578
+ Validate dict structure against rules:
579
+
580
+ ```text
581
+ # Conceptual - uses dynamic existence checks and dict spread
582
+ [
583
+ name: [type: "string", required: true, minLen: 1],
584
+ age: [type: "number", required: true, min: 0, max: 150],
585
+ email: [type: "string", required: false]
586
+ ] :> $schema
587
+
588
+ [name: "Alice", age: 200] :> $data
589
+
590
+ $schema.entries -> fold([valid: true, errors: []]) {
591
+ $[0] :> $field
592
+ $[1] :> $rules
593
+
594
+ # Check required
595
+ ($rules.required && ($data.?($field) -> !)) ? {
596
+ [valid: false, errors: [...$@.errors, "{$field} is required"]]
597
+ } ! {
598
+ # Check type and constraints if field exists
599
+ $data.?($field) ? {
600
+ $data.($field) :> $value
601
+
602
+ # Type check
603
+ (type($value) != $rules.type) ? {
604
+ [valid: false, errors: [...$@.errors, "{$field} must be {$rules.type}"]]
605
+ } ! {
606
+ # Range check for numbers
607
+ ($rules.type == "number") ? {
608
+ ($rules.?min && $value < $rules.min) ? {
609
+ [valid: false, errors: [...$@.errors, "{$field} below minimum"]]
610
+ } ! ($rules.?max && $value > $rules.max) ? {
611
+ [valid: false, errors: [...$@.errors, "{$field} above maximum"]]
612
+ } ! $@
613
+ } ! $@
614
+ }
615
+ } ! $@
616
+ }
617
+ }
618
+ # Result: [valid: false, errors: ["age above maximum"]]
619
+ ```
620
+
621
+ ---
622
+
623
+ ## See Also
624
+
625
+ - [Reference](11_reference.md) — Complete language specification
626
+ - [Collections](07_collections.md) — `each`, `map`, `filter`, `fold` details
627
+ - [Closures](06_closures.md) — Function patterns and binding
628
+ - [Parsing](10_parsing.md) — Text extraction utilities