plum

#treesitter#compiler#wasm

git clone https://git.pyrossh.dev/plum

A statically typed, imperative programming language inspired by rust, python


094fd48 — Peter John 2026-10-26T15:41:36+05:30
docs(plans): T9 fun* iterators with *T and yield; range moves to std
docs/plans/2026-10-26-zig-generics-and-numbers.md CHANGED
@@ -208,3 +208,23 @@ Tests:
208
208
  - `not_a_form_error`.
209
209
 
210
210
  Bench: a loop over 10k units doing filter, then map, then fold. Compare the closure-method chain against the fused `for` form, and record the wasm size difference.
211
+
212
+ ## Task T9: `fun*` iterators with `*T` and `yield` (user decisions; runs after the T8 review)
213
+
214
+ - **Declaration** (the user chose `fun*`): `fun* name[..](params) *T = …` or `fun* name[..](params) *(A, B) -> R = …`. `*T`/`*(A, B)` is what each step yields to the `for` binders, and the arity must equal the binder count. `-> R` is the optional result of the whole `for` (Unit if absent).
215
+ - `*T` is legal only on a `fun*`, and a `fun*` must have one: `fun* 'f' must declare its yield type (*T)` / `*T is only allowed on fun*`.
216
+ - `fun*` replaces `inline fun` plus the `block(...)` param: the `inline` keyword and the `block` param type are removed from the language, with migration hints in `legacySyntaxHint`.
217
+ - **`yield a, b`** is a statement, or an expression whose value is the user body's last expression. The checker infers the body's required type from how the `fun*` uses the yield value (`if yield x` gives Bool, `acc = yield acc, x` gives A, and an unused value gives anything). Expansion semantics are T8's: hygienic, one loop, no closure, no recursion, no `return` inside a `fun*`, and break/continue target the innermost loop around the `yield`.
218
+ - A `fun*` can only be used as a `for` source (`fun* 'f' can only be used as a for source`).
219
+ - **`range` moves to std** as `fun* range(start: Int, end: Int) *Int`, and the `for i := range n` sugar is removed (the user decided). Ruling: there is a single two-arg form, `range(0, n)`, because Plum has no overloading and keyword args are still matched by position. All ~67 uses in std, examples and tests migrate. The compiler's special range loop is deleted, so the only loop primitives left are `while` and `for` over a `fun*` or an iterable.
220
+ - **plum-std/Iter.plum** is rewritten with `fun*` and `yield`: `range`, `filter` (`*T -> List[T]`), `map` (`*T -> List[U]`), `find` (`*T -> T?`), `fold` (`*(A, T) -> A`), `any`, `all`, `count`, `indexed` (`*(Int, T)`), `zip` (`*(A, B)`), `each`.
221
+
222
+ Tests:
223
+ - `fun_star_range`, `yield_value_drives_filter` and `fold_yield_two`;
224
+ - `star_type_only_on_fun_star`, `fun_star_requires_star_type` and `fun_star_only_as_for_source`;
225
+ - `inline_keyword_removed_hint` and `range_sugar_removed_hint` (`for i := range n` gets the hint "write range(0, n)");
226
+ - `no_closure_in_expansion` (wasm inspection);
227
+ - `nested_fun_star_forms`;
228
+ - `binder_arity_mismatch`.
229
+
230
+ Update the README iteration section and the formatter/highlight queries.