diff --git a/NEXT.md b/NEXT.md index bdfa208..aaa2bee 100644 --- a/NEXT.md +++ b/NEXT.md @@ -590,6 +590,43 @@ Two smaller findings, both written down beside the code that ran into them: Already present and easy to miss: an **EDN parser**, at `vendor/edn/edn.flan`. +## Queued: `into`, fused transformation without transducers + +Decided in conversation. **Not transducers, and not Rust's iterators — a macro that fuses the chain at compile time.** + +``` +(into xs (vec-new i32) (map double) (filter even?)) +``` + +Argument order is **source, destination, then any number of transforms**, matching the `into->` macro the author +already uses in Clojure (`from to xform & xforms`). It reads as a sentence — take this, put it there, doing these — +and the variadic transforms have to trail anyway, which is the mechanical reason they cannot sit in the middle. +Clojure's own `into` composes them into one `xform` first, which is why that macro exists at all. + +**Why a macro and not transducers.** Transducers compose at *runtime*: they need function values, closures and +allocation, and every element pays a chain of indirect calls. Rust has no transducers — it has iterators, which are +lazy but fuse into a single loop at compile time via monomorphisation and inlining, needing generics to do it. A +macro reaches the same destination with neither: `(map double xs)` expands to `(double x)` written straight into the +loop body, so the function name is *syntax* and never a value. **No intermediate collection at any step, no closure, +no generics, and nothing to inline.** + +**It therefore does not need function values** and is independent of that work. + +**What it gives up, and the author does not want it anyway:** you cannot build a transformation at runtime and pass it +around. That is transducers' actual selling point and it is close to useless in a game. + +**Why the destination belongs in the form, and why this suits Flan better than `->>` would.** Every collecting +operation here allocates from an *explicit* allocator — that is a frozen rule in `spec-memory.md`. `->>` hides where +the result goes; `into` names it, so the macro knows the destination type, emits the right loop and the right +allocation, and the rule is honoured by construction. plan.org's `->>` threading over slices is the thing this +replaces for the collecting cases. + +**Open, and worth settling when it is built:** whether reductions share the form. `(into xs 0 (map cost) (sum))` +reads oddly because zero is not a collection. A second macro with the same shape may be cleaner, so that the +destination is always honest about what it is. + +Drop Clojure's `:eduction` branch — that is the pass-around case, and the one part that would need runtime machinery. + ## The next batch, in order Agreed at the end of 2026-09-12. Ordered by priority, not by size. Items 1-3 and 5-6 want the compiler core and should