@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.
- package/.github/skills/yo-async-effects/SKILL.md +15 -15
- package/.github/skills/yo-async-effects/async-effects-recipes.md +110 -121
- package/.github/skills/yo-core-patterns/core-patterns-cheatsheet.md +3 -0
- package/.github/skills/yo-project-workflow/workflow-cheatsheet.md +2 -0
- package/.github/skills/yo-syntax/SKILL.md +2 -2
- package/.github/skills/yo-syntax/syntax-cheatsheet.md +195 -73
- package/README.md +2 -0
- package/out/cjs/index.cjs +624 -613
- package/out/cjs/yo-cli.cjs +739 -727
- package/out/cjs/yo-lsp.cjs +636 -625
- package/out/esm/index.mjs +526 -515
- package/out/types/src/codegen/functions/declarations.d.ts +1 -1
- package/out/types/src/doc/model.d.ts +0 -1
- package/out/types/src/env.d.ts +1 -2
- package/out/types/src/evaluator/calls/helper.d.ts +4 -2
- package/out/types/src/evaluator/context.d.ts +1 -1
- package/out/types/src/evaluator/exprs/{escape.d.ts → unwind.d.ts} +1 -1
- package/out/types/src/evaluator/types/function.d.ts +1 -2
- package/out/types/src/evaluator/types/synthesizer.d.ts +1 -0
- package/out/types/src/evaluator/utils.d.ts +0 -1
- package/out/types/src/expr.d.ts +5 -6
- package/out/types/src/test-runner.d.ts +2 -0
- package/out/types/src/types/creators.d.ts +4 -6
- package/out/types/src/types/definitions.d.ts +7 -16
- package/out/types/src/types/guards.d.ts +1 -2
- package/out/types/src/types/tags.d.ts +0 -1
- package/out/types/src/types/utils.d.ts +1 -0
- package/out/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/scripts/probe-parser-parity.ts +61 -0
- package/scripts/probe-yo-self-parser.sh +33 -0
- package/scripts/validate-yo-self-fmt.ts +184 -0
- package/std/async.yo +1 -1
- package/std/crypto/random.yo +6 -6
- package/std/encoding/base64.yo +4 -4
- package/std/encoding/hex.yo +2 -2
- package/std/encoding/json.yo +3 -3
- package/std/encoding/utf16.yo +1 -1
- package/std/error.yo +14 -2
- package/std/fs/dir.yo +56 -62
- package/std/fs/file.yo +118 -124
- package/std/fs/metadata.yo +11 -17
- package/std/fs/temp.yo +21 -27
- package/std/fs/walker.yo +10 -16
- package/std/http/client.yo +25 -29
- package/std/http/index.yo +4 -4
- package/std/io/reader.yo +1 -1
- package/std/io/writer.yo +2 -2
- package/std/net/addr.yo +1 -1
- package/std/net/dns.yo +10 -14
- package/std/net/errors.yo +1 -1
- package/std/net/tcp.yo +67 -71
- package/std/net/udp.yo +36 -40
- package/std/os/signal.yo +2 -2
- package/std/prelude.yo +27 -21
- package/std/process/command.yo +32 -38
- package/std/regex/parser.yo +10 -10
- package/std/sys/bufio/buf_reader.yo +14 -14
- package/std/sys/bufio/buf_writer.yo +17 -17
- package/std/sys/errors.yo +1 -1
- package/std/thread.yo +2 -2
- package/std/url/index.yo +2 -2
- 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 `
|
|
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: `(
|
|
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
|
-
###
|
|
183
|
+
### Effect parameters (explicit)
|
|
172
184
|
|
|
173
185
|
```rust
|
|
174
|
-
Raise :: (
|
|
186
|
+
Raise :: (ctl(msg : String) -> i32);
|
|
175
187
|
|
|
176
|
-
safe_divide :: (fn(x : i32, y : 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
|
-
|
|
185
|
-
|
|
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
|
-
-
|
|
193
|
-
- `
|
|
194
|
-
|
|
195
|
-
-
|
|
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(
|
|
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
|
-
|
|
257
|
-
.
|
|
258
|
-
|
|
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
|
-
### `
|
|
729
|
+
### `unwind` requires a nested-function context
|
|
686
730
|
|
|
687
|
-
`
|
|
688
|
-
|
|
689
|
-
(
|
|
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
|
-
|
|
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
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
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 —
|
|
707
|
-
|
|
708
|
-
|
|
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**:
|
|
713
|
-
|
|
714
|
-
|
|
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
|
|
773
|
-
|
|
|
774
|
-
| Higher-Kinded Types
|
|
775
|
-
| GADTs
|
|
776
|
-
| Derive traits
|
|
777
|
-
| Type reflection
|
|
778
|
-
| Inline assembly
|
|
779
|
-
| Metaprogramming
|
|
780
|
-
| Effect
|
|
781
|
-
| Custom derive rules
|
|
782
|
-
| Isolated types
|
|
783
|
-
| Arc (atomic ref count)
|
|
784
|
-
| Parallelism
|
|
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