@shd101wyy/yo 0.1.27 → 0.1.29

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 (63) hide show
  1. package/.github/skills/yo-async-effects/SKILL.md +15 -15
  2. package/.github/skills/yo-async-effects/async-effects-recipes.md +110 -121
  3. package/.github/skills/yo-core-patterns/core-patterns-cheatsheet.md +3 -0
  4. package/.github/skills/yo-project-workflow/workflow-cheatsheet.md +2 -0
  5. package/.github/skills/yo-syntax/SKILL.md +2 -2
  6. package/.github/skills/yo-syntax/syntax-cheatsheet.md +195 -73
  7. package/README.md +2 -0
  8. package/out/cjs/index.cjs +624 -613
  9. package/out/cjs/yo-cli.cjs +739 -727
  10. package/out/cjs/yo-lsp.cjs +636 -625
  11. package/out/esm/index.mjs +526 -515
  12. package/out/types/src/codegen/functions/declarations.d.ts +1 -1
  13. package/out/types/src/doc/model.d.ts +0 -1
  14. package/out/types/src/env.d.ts +1 -2
  15. package/out/types/src/evaluator/calls/helper.d.ts +4 -2
  16. package/out/types/src/evaluator/context.d.ts +1 -1
  17. package/out/types/src/evaluator/exprs/{escape.d.ts → unwind.d.ts} +1 -1
  18. package/out/types/src/evaluator/types/function.d.ts +1 -2
  19. package/out/types/src/evaluator/types/synthesizer.d.ts +1 -0
  20. package/out/types/src/evaluator/utils.d.ts +0 -1
  21. package/out/types/src/expr.d.ts +5 -6
  22. package/out/types/src/test-runner.d.ts +2 -0
  23. package/out/types/src/types/creators.d.ts +4 -6
  24. package/out/types/src/types/definitions.d.ts +7 -16
  25. package/out/types/src/types/guards.d.ts +1 -2
  26. package/out/types/src/types/tags.d.ts +0 -1
  27. package/out/types/src/types/utils.d.ts +1 -0
  28. package/out/types/tsconfig.tsbuildinfo +1 -1
  29. package/package.json +1 -1
  30. package/scripts/probe-parser-parity.ts +61 -0
  31. package/scripts/probe-yo-self-parser.sh +33 -0
  32. package/scripts/validate-yo-self-fmt.ts +184 -0
  33. package/std/async.yo +1 -1
  34. package/std/crypto/random.yo +6 -6
  35. package/std/encoding/base64.yo +4 -4
  36. package/std/encoding/hex.yo +2 -2
  37. package/std/encoding/json.yo +3 -3
  38. package/std/encoding/utf16.yo +1 -1
  39. package/std/error.yo +14 -2
  40. package/std/fs/dir.yo +56 -62
  41. package/std/fs/file.yo +118 -124
  42. package/std/fs/metadata.yo +11 -17
  43. package/std/fs/temp.yo +21 -27
  44. package/std/fs/walker.yo +10 -16
  45. package/std/http/client.yo +25 -29
  46. package/std/http/index.yo +4 -4
  47. package/std/io/reader.yo +1 -1
  48. package/std/io/writer.yo +2 -2
  49. package/std/net/addr.yo +1 -1
  50. package/std/net/dns.yo +10 -14
  51. package/std/net/errors.yo +1 -1
  52. package/std/net/tcp.yo +67 -71
  53. package/std/net/udp.yo +36 -40
  54. package/std/os/signal.yo +2 -2
  55. package/std/prelude.yo +27 -21
  56. package/std/process/command.yo +32 -38
  57. package/std/regex/parser.yo +10 -10
  58. package/std/sys/bufio/buf_reader.yo +14 -14
  59. package/std/sys/bufio/buf_writer.yo +17 -17
  60. package/std/sys/errors.yo +1 -1
  61. package/std/thread.yo +2 -2
  62. package/std/url/index.yo +2 -2
  63. package/std/worker.yo +2 -2
@@ -78,7 +78,17 @@ if(done, println("done"), println("pending"));
78
78
  - Always write `match(...)`, never bare `match ...`
79
79
  - `if(a, b)` and `if(a, b, c)` are macro forms over `cond`
80
80
  - Write `return(value)` or `return()`; `return value` is invalid.
81
- - Write `escape(value)` or `escape()`; `escape value` is invalid.
81
+ - Write `unwind(value)` or `unwind()`; `unwind value` is invalid.
82
+ - If a `match`/`cond` branch returns an enum variant and inference fails, qualify
83
+ the variant with its enum type: `TypeValue.Unit` instead of `.Unit`.
84
+ - Do not match enum payload literals directly, e.g. avoid `.Some(false)` and
85
+ `.Some(true)` as sibling branches. Match `.Some(value)` once, then branch with
86
+ `if(value, ...)` or `cond(...)` inside the arm; otherwise generated C can
87
+ contain duplicate enum `case` labels.
88
+ - In large enum matches, avoid binding a pattern variable with the same name as a
89
+ variant field (for example, prefer `struct_field_types` over `field_types`).
90
+ This can currently produce invalid generated C in some self-hosted codegen
91
+ paths.
82
92
 
83
93
  ## String types
84
94
 
@@ -111,7 +121,7 @@ masked := ((A | B) | C);
111
121
  - Yo has no operator precedence; fully parenthesize binary expressions
112
122
  - Preserve grouping around infix expressions on operator RHS positions: `true => (x / y)`, `value := (x + y)`, `(ptr &+ 1).*`
113
123
  - Line breaks can disambiguate operator chains; keep line-leading operators like `(4\n| 5\n| 6)` and newlines after `:` before a lambda unless you add equivalent grouping
114
- - When an operator ends a line, indent its RHS one level as a continuation: `(given(x) : T) =\n (v) -> { ... }`
124
+ - When an operator ends a line, indent its RHS one level as a continuation: `(x : T) =\n (v) -> { ... }`
115
125
  - Prefix operators (`!`, `&`, `-`, `~`) require parenthesized operands: `func(&(s), a, b)`, `!(ready)`, `-(value)`.
116
126
  - Tight special forms also require immediate parentheses: `#(expr)`, `?*(u8)`, `T <: !(Runtime)`
117
127
  - Dynamic field access with unquote must keep grouping after the dot: `value.(#(field_expr))`, not `value.#(field_expr)`.
@@ -140,6 +150,8 @@ impl(Counter,
140
150
  ```
141
151
 
142
152
  - No space between a function type and its body: `(fn(...) -> T)(...)`
153
+ - Top-level aliases for function types need parentheses too:
154
+ `Callback :: (fn(x : i32) -> i32);`, not `Callback :: fn(x : i32) -> i32;`
143
155
  - Use `Self` in method signatures and in type definitions for recursive references (the type name is not available during its own definition)
144
156
  - `Self` also works inside generic type constructors — it refers to the current instantiation (e.g., `Tree(T)` inside `Tree`). Use `recur(args)` only when type arguments differ from the current instantiation.
145
157
  - Use `struct(...)` for record and effect-record types. The legacy `module(...)`,
@@ -168,12 +180,12 @@ create_user(name: `Bob`, age: 30);
168
180
  - Named arguments must keep the same order as the definition
169
181
  - Default values use `?=` and must be compile-time known
170
182
 
171
- ### Implicit parameters (`using` / `given`)
183
+ ### Effect parameters (explicit)
172
184
 
173
185
  ```rust
174
- Raise :: (fn(msg : String) -> i32);
186
+ Raise :: (ctl(msg : String) -> i32);
175
187
 
176
- safe_divide :: (fn(x : i32, y : i32, using(raise : Raise)) -> i32)(
188
+ safe_divide :: (fn(x : i32, y : i32, raise : Raise) -> i32)(
177
189
  cond(
178
190
  (y == i32(0)) => raise(`divide by zero`),
179
191
  true => (x / y)
@@ -181,18 +193,20 @@ safe_divide :: (fn(x : i32, y : i32, using(raise : Raise)) -> i32)(
181
193
  );
182
194
 
183
195
  caller :: (fn() -> i32)({
184
- (given(raise) : Raise) = (fn(msg : String) -> i32)({
185
- return(i32(0));
196
+ // Handler value bound to a local. Lambdas on the RHS of `=` need outer parens.
197
+ (raise : Raise) = ((msg) -> {
198
+ unwind(i32(0));
186
199
  });
187
200
 
188
- safe_divide(i32(10), i32(0))
201
+ safe_divide(i32(10), i32(0), raise)
189
202
  });
190
203
  ```
191
204
 
192
- - `using(name : Type)` declares an implicit parameter (effect)
193
- - `given(name) := Type(fields...)` installs a handler in the caller's scope
194
- - Effects are matched by **type**, not by name
195
- - The handler is auto-resolved at call sites; pass explicitly with `using(name)`
205
+ - Effect handlers are regular parameters pass them explicitly at the call site.
206
+ - `ctl(args) -> R` types a handler that may `unwind` (discard the continuation).
207
+ Use plain `fn(args) -> R` for handlers that always resume.
208
+ - Bundle multiple effects into a struct (`Ctx :: struct(raise : Raise, log : Log)`)
209
+ and pass one parameter when there are many.
196
210
 
197
211
  ### Closures and anonymous functions
198
212
 
@@ -250,15 +264,28 @@ text := match(value,
250
264
  Three destructuring shapes for arms (mix freely across arms):
251
265
 
252
266
  ```rust
253
- Shape :: enum(Circle(radius : i32), Rectangle(width : i32, height : i32));
267
+ Shape :: enum(
268
+ Circle(radius : i32),
269
+ Rectangle(width : i32, height : i32),
270
+ Triangle(base : i32, height : i32, label : str)
271
+ );
254
272
 
255
273
  match(s,
256
- .Circle(r) => (r * r), // positional
257
- .Rectangle(width: w, height: h) => (w * h), // labeled
258
- .Rectangle({width, height: h}) => (width * h) // curly shorthand
274
+ // Preferred curly shorthand names only the fields you use.
275
+ .Triangle({base, height: h}) => (base * h),
276
+
277
+ // Also OK — labeled (label : var) pairs; order-free, partial matches OK.
278
+ .Circle(radius: r) => (r * r),
279
+
280
+ // ⚠️ Avoid for 2+ field variants — positional with `_` is brittle when
281
+ // a field is added and harder to read (each `_` requires counting).
282
+ // OK when the variant has one field, or when every field is named.
283
+ .Rectangle(w, h) => (w * h)
259
284
  )
260
285
  ```
261
286
 
287
+ **Preferred form**: `.Variant({label, label: alias})`. Names only the fields the arm binds, so adding a field to the variant later doesn't silently break every arm. `tests/match_curly.test.yo` is the spec.
288
+
262
289
  Curly `{a, b: c}` is sugar for `(a: a, b: c)` — order-free, supports partial matches (omit fields). Use `{label: _}` to ignore a specific field. Bare `{_}` and empty `{}` are rejected.
263
290
 
264
291
  > **Critical**: Within a single match arm, you must use **either all positional or all named** field patterns. Mixing positional and named fields in the same arm (e.g., `.Foo(x, y: z, w)`) causes C codegen to emit undeclared identifiers for the named fields. This is a parser/codegen limitation — do not mix.
@@ -553,6 +580,23 @@ Variable :: object(name : String, type : TypeValue);
553
580
  Variable :: object(name : String, ty : TypeValue);
554
581
  ```
555
582
 
583
+ ### 1-element array literals require a trailing comma
584
+
585
+ `[expr]` without a trailing comma is **parsed as a Slice type** `Slice(expr)`, not an array literal. To create a 1-element array value, add a trailing comma:
586
+
587
+ ```rust
588
+ // WRONG — parsed as Slice type, not array literal:
589
+ arr := [i32(42)];
590
+
591
+ // CORRECT — trailing comma makes it an array literal:
592
+ arr := [i32(42),];
593
+
594
+ // Multi-element arrays work fine (comma separator detected):
595
+ arr2 := [i32(1), i32(2), i32(3)]; // ✓
596
+ ```
597
+
598
+ This also applies inside source strings in proto-evaluator tests.
599
+
556
600
  ### ArrayList indexing uses call syntax
557
601
 
558
602
  ```rust
@@ -682,39 +726,38 @@ if(!cond, { do_thing(); });
682
726
  if((!cond), { do_thing(); });
683
727
  ```
684
728
 
685
- ### `escape` requires a nested-function context
729
+ ### `unwind` requires a nested-function context
686
730
 
687
- `escape(value)` exits the **enclosing function** — the nearest `fn(...)` that
688
- wraps the current code. It requires that the code is inside a nested function
689
- (e.g., a closure or `given` handler lambda), NOT at the top level of a
690
- standalone function definition.
731
+ `unwind(value)` exits the **install frame** — the function that bound the
732
+ `ctl(...) -> R` value being called. It is only valid inside the body of a
733
+ `ctl(...) -> R` value (an effect handler).
691
734
 
692
735
  ```rust
693
- // CORRECT escape inside a given handler lambda (lambda has enclosing fn):
694
- given(exn) := Exception(throw: ((err) -> {
695
- escape(result); // exits the outer function that contains this given()
696
- }));
697
- do_something(using(exn));
736
+ Raise :: (ctl(msg : String) -> i32);
698
737
 
699
- // CORRECT escape inside a closure passed as argument:
700
- result := match(opt, .Some(x) => x, .None => {
701
- // This is NOT a nested function — use return:
702
- // WRONG: escape(default_val) // ERROR: no enclosing fn
703
- return(default_val); // CORRECT: return exits the enclosing fn directly
738
+ caller :: (fn() -> i32)({
739
+ // The handler is a `ctl` value bound in `caller`. `unwind` exits `caller`.
740
+ (raise : Raise) = ((msg) -> {
741
+ eprintln(msg);
742
+ unwind(i32(-1));
743
+ });
744
+ safe_divide(i32(10), i32(0), raise) // call site: handler is passed explicitly
704
745
  });
705
746
 
706
- // WRONG — escape inside a match arm (not a nested fn):
707
- match(opt,
708
- .Some(x) => { escape(x); } // ERROR: "can only be used inside a function that has an enclosing function"
747
+ // WRONG — `unwind` in a regular `fn` body (no install frame here) is rejected.
748
+ bad :: (fn() -> unit)({
749
+ unwind(()); // ERROR: unwind requires a ctl(...) body
750
+ });
751
+
752
+ // WRONG — capturing a `ctl` value into a closure is rejected (closures escape).
753
+ make_closure :: (fn(raise : Raise) -> Impl(Fn() -> unit))(
754
+ () => { raise(`x`); } // ERROR: closure captures a control-bound value
709
755
  );
710
756
  ```
711
757
 
712
- **Rule of thumb**: Use `escape` only inside lambdas passed to `given()` handlers.
713
- In all other contexts (match arms, if blocks, begin blocks, top-level fn bodies),
714
- use `return` for early exit.
715
-
716
- `return` inside a lambda exits that lambda only; `escape` exits the OUTER function
717
- (the one that installed the `given` handler).
758
+ **Rule of thumb**: `unwind` belongs only inside the lambda bound to a
759
+ `ctl(...) -> R` handler. From any other position, use `return` to exit the
760
+ current `fn`.
718
761
 
719
762
  ### Parameter reassignment
720
763
 
@@ -769,19 +812,19 @@ fn_taking_str((`prefix_${value}`).as_str());
769
812
 
770
813
  These features are powerful but less commonly used. Consult the linked docs for full details.
771
814
 
772
- | Feature | Syntax hint | Documentation |
773
- | ---------------------- | -------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
774
- | Higher-Kinded Types | `forall(F : (fn(comptime(T) : Type) -> comptime(Type)))` | [DESIGN.md § HKT](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/DESIGN.md#higher-kinded-types-hkt) |
775
- | GADTs | `enum(IntVal(i : i32) -> recur(i32))` | [GADTS.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/GADTS.md) |
776
- | Derive traits | `derive(MyType, Eq, Hash, Clone, Ord, ToString)` | [DERIVE_TRAITS.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/DERIVE_TRAITS.md) |
777
- | Type reflection | `Type.get_info(T)` returns `TypeInfo` | [TYPE_REFLECTION.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/TYPE_REFLECTION.md) |
778
- | Inline assembly | `asm("mov {0}, #42", out(reg, i32))` | [INLINE_ASSEMBLY.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/INLINE_ASSEMBLY.md) |
779
- | Metaprogramming | `quote(...)`, `unquote(...)`, `unquote_splicing(...)` | [DESIGN.md § Meta](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/DESIGN.md#meta-programming) |
780
- | Effect row variables | `forall(...(E))` with `using(...(E))` | [ALGEBRAIC_EFFECTS.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/ALGEBRAIC_EFFECTS.md) |
781
- | Custom derive rules | `derive_rule(MyTrait, (fn(...) -> unquote(Expr)){...})` | [DERIVE_TRAITS.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/DERIVE_TRAITS.md#user-defined-derive-rules) |
782
- | Isolated types | `Iso(T)` for data-race-free parallelism | [ISOLATED.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/ISOLATED.md) |
783
- | Arc (atomic ref count) | `arc(value)`, `shared.(*)` for cross-thread sharing | [ARC.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/ARC.md) |
784
- | Parallelism | Thread pool, `io.spawn` for parallel work | [PARALLELISM.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/PARALLELISM.md) |
815
+ | Feature | Syntax hint | Documentation |
816
+ | -------------------------- | -------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
817
+ | Higher-Kinded Types | `forall(F : (fn(comptime(T) : Type) -> comptime(Type)))` | [DESIGN.md § HKT](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/DESIGN.md#higher-kinded-types-hkt) |
818
+ | GADTs | `enum(IntVal(i : i32) -> recur(i32))` | [GADTS.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/GADTS.md) |
819
+ | Derive traits | `derive(MyType, Eq, Hash, Clone, Ord, ToString)` | [DERIVE_TRAITS.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/DERIVE_TRAITS.md) |
820
+ | Type reflection | `Type.get_info(T)` returns `TypeInfo` | [TYPE_REFLECTION.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/TYPE_REFLECTION.md) |
821
+ | Inline assembly | `asm("mov {0}, #42", out(reg, i32))` | [INLINE_ASSEMBLY.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/INLINE_ASSEMBLY.md) |
822
+ | Metaprogramming | `quote(...)`, `unquote(...)`, `unquote_splicing(...)` | [DESIGN.md § Meta](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/DESIGN.md#meta-programming) |
823
+ | Effect bundle polymorphism | `forall(E : Type.Struct)` over a bundle struct | [ALGEBRAIC_EFFECTS.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/ALGEBRAIC_EFFECTS.md) |
824
+ | Custom derive rules | `derive_rule(MyTrait, (fn(...) -> unquote(Expr)){...})` | [DERIVE_TRAITS.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/DERIVE_TRAITS.md#user-defined-derive-rules) |
825
+ | Isolated types | `Iso(T)` for data-race-free parallelism | [ISOLATED.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/ISOLATED.md) |
826
+ | Arc (atomic ref count) | `arc(value)`, `shared.(*)` for cross-thread sharing | [ARC.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/ARC.md) |
827
+ | Parallelism | Thread pool, `io.spawn` for parallel work | [PARALLELISM.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/PARALLELISM.md) |
785
828
 
786
829
  ---
787
830
 
@@ -948,26 +991,6 @@ Always merge them into a single destructuring import:
948
991
  { Foo, Bar } :: import("../../mod.yo");
949
992
  ```
950
993
 
951
- ### Implicit (`using`) parameters cannot be used with `:=` assignment
952
-
953
- Implicit effect parameters introduced via `using(name : Type)` cannot be bound
954
- to a discarded variable with `:= name`. They can only be passed via `using()`.
955
- To suppress "unused parameter" warnings for an implicit param, simply omit the
956
- discard assignment — implicit params never trigger unused-variable errors.
957
-
958
- ```rust
959
- // WRONG — implicit variable cannot be used in assignment:
960
- foo :: (fn(using(exn : Exception)) -> unit)({
961
- _ := exn; // ERROR: Cannot use implicit variable "exn" in assignment
962
- ()
963
- });
964
-
965
- // CORRECT — just omit the discard line:
966
- foo :: (fn(using(exn : Exception)) -> unit)({
967
- ()
968
- });
969
- ```
970
-
971
994
  ### Nested `Option` patterns require staging
972
995
 
973
996
  `match` does not support nested destructuring patterns like `.Some(.TypeVal(x))`.
@@ -1036,3 +1059,102 @@ match(outer_val,
1036
1059
  .None => { fallback() } // ← outer .None arm
1037
1060
  ) // ← closes outer match
1038
1061
  ```
1062
+
1063
+ ### Nested enum patterns in match are NOT supported
1064
+
1065
+ Yo does **not** support nested enum patterns inside a single match arm.
1066
+ You cannot write `.Some(.IntLit(n))` — this is a parser error.
1067
+
1068
+ ```rust
1069
+ // ❌ WRONG — nested enum pattern, parser error:
1070
+ match(v.get(usize(0)),
1071
+ .Some(.IntLit(n)) => assert(n.as_str() == "3", "ok"),
1072
+ _ => assert(false, "err")
1073
+ )
1074
+
1075
+ // ✅ CORRECT — two-level match:
1076
+ match(v.get(usize(0)),
1077
+ .Some(x) => match(x, .IntLit(n) => assert(n.as_str() == "3", "ok"), _ => assert(false, "err")),
1078
+ .None => assert(false, "err")
1079
+ )
1080
+ ```
1081
+
1082
+ This applies to ALL nested enum patterns: `.Some(.BoolVal(b))`, `.Some(.ArrayVal(arr))`, etc. — always use a two-level match.
1083
+
1084
+ ### `get_callee()` returns ExprVal directly, not an Option-wrapped EnumVal
1085
+
1086
+ In the proto-evaluator source strings (`evaluate_module_body`), `ExprVal.get_callee()` on a FnCall returns the callee `ExprVal` directly — NOT wrapped in an `Option` EnumVal. Chaining `.is_some()` fails with SIGABRT because `is_some()` requires an `EnumVal` receiver.
1087
+
1088
+ ```rust
1089
+ // ❌ SIGABRT — get_callee() returns ExprVal, not Option(EnumVal)
1090
+ result := quote(foo(i64(1))).get_callee().is_some();
1091
+
1092
+ // ✅ Chain .is_atom() or .is_fn_call() on the returned ExprVal
1093
+ result := quote(foo(i64(1))).get_callee().is_atom(); // true: callee "foo" is an atom
1094
+ result := quote(foo(i64(1))).get_callee().is_fn_call(); // false: callee "foo" is not a fn call
1095
+ ```
1096
+
1097
+ Similarly, calling `get_callee()` on an Atom causes the overall evaluation to fail — do not test the Atom case via `get_callee()` in source strings.
1098
+
1099
+ ### Source-string evaluation pitfalls (proto-evaluator tests)
1100
+
1101
+ When writing source strings passed to `evaluate_module_body` in proto-evaluator tests:
1102
+
1103
+ **`cond` form**: Always use the `cond(condition => value, true => fallback)` form, NOT `cond(condition, value, fallback)`. The 3-arg form does NOT work inside lambdas or recursive functions in source strings.
1104
+
1105
+ ```
1106
+ // ❌ WRONG — crashes inside lambdas and recursive functions
1107
+ cond((n <= i32(1)), i32(1), (n * recur((n - i32(1)))))
1108
+
1109
+ // ✅ CORRECT
1110
+ cond((n <= i32(1)) => i32(1), true => (n * recur((n - i32(1)))))
1111
+ ```
1112
+
1113
+ **Recursive functions**: Use `recur(...)` for self-recursion inside named `::` functions. Never call the function by name from inside its own body.
1114
+
1115
+ **Chaining function calls with operators**: `f(a) + f(b) + f(c)` throws an exception. Use fold over an array instead:
1116
+
1117
+ ```
1118
+ // ❌ WRONG — exception in source strings
1119
+ result := abs_val(i32(3)) + abs_val(i32(1)) + abs_val(i32(4));
1120
+
1121
+ // ✅ CORRECT
1122
+ arr := [i32(3), i32(1), i32(4)];
1123
+ result := arr.fold(i32(0), (fn(acc : i32, x : i32) -> i32)((acc + abs_val(x))));
1124
+ ```
1125
+
1126
+ **Empty array `[]` in cond branches**: `cond(condition => [x], true => [])` crashes because the empty array type is unknown. Avoid empty array literals in conditional branches inside `flat_map` lambdas.
1127
+
1128
+ **Option types**: Must use `Option(T).Some(val)` not `Option.Some(val)`. `Option(T).None` with a type annotation crashes — use `r := Option(i32).None` without annotation. `.is_none()` is not supported; use `!(r.is_some())`. `and_then(f)` returns the raw value (not wrapped in Option), so calling `.unwrap_or()` on the result crashes.
1129
+
1130
+ **Number literals**: `i32(-3)` crashes — use `(i32(0) - i32(3))`. `i32.as_usize()` / `usize.as_i32()` not supported.
1131
+
1132
+ **Fibonacci without tmp variable**: `b = (a + b); a = (b - a)` computes fib correctly without a temp variable. After N iterations, `a` holds fib(N) and `b` holds fib(N+1).
1133
+
1134
+ **3-term multiplication in source strings**: `(x * x * x)` causes an exception in evaluated source strings. Break it into a block:
1135
+
1136
+ ```
1137
+ // ❌ WRONG — causes exception
1138
+ cubes := arr.map((fn(x : i32) -> i32)((x * x * x)));
1139
+
1140
+ // ✅ CORRECT — use a block with a local binding
1141
+ cubes := arr.map((fn(x : i32) -> i32)({
1142
+ sq := (x * x);
1143
+ (sq * x)
1144
+ }));
1145
+ ```
1146
+
1147
+ **3-term sum in fold on tuples**: `(acc + p.0 + p.1)` inside a fold lambda on tuple pairs crashes. Always map pairs to scalars first, then fold:
1148
+
1149
+ ```
1150
+ // ❌ WRONG — crashes in fold on (i32, i32) tuples
1151
+ total := pairs.fold(i32(0), (fn(acc : i32, p : (i32, i32)) -> i32)((acc + p.0 + p.1)));
1152
+
1153
+ // ✅ CORRECT — map to scalars first, then fold
1154
+ sums := pairs.map((fn(p : (i32, i32)) -> i32)((p.0 + p.1)));
1155
+ total := sums.fold(i32(0), (fn(acc : i32, x : i32) -> i32)((acc + x)));
1156
+ ```
1157
+
1158
+ **`&&` in `cond` conditions inside `while` body**: Crashes. Avoid by restructuring (e.g., start loop at 1 instead of 0 to eliminate the `&& (i > 0)` guard).
1159
+
1160
+ **Test API format**: Use `evaluate_module_body(exprs, &(env))` (reference syntax, returns `Option`). Match with function-style `match(result, .None => ..., .Some(m) => ...)`. Do NOT use block-style `match(result) { ... }` — it causes a parse error ("Paren-less function and operator calls are not supported").
package/README.md CHANGED
@@ -8,6 +8,8 @@
8
8
 
9
9
  https://shd101wyy.github.io/Yo
10
10
 
11
+ **LLM-friendly to write, human-friendly to read.**
12
+
11
13
  A multi-paradigm, general-purpose, compiled programming language.
12
14
  Yo aims to be **Simple** and **Fast** (around 0% - 15% slower than C).
13
15