into fuses a chain at compile time, which is what transducers are for without being them
This commit is contained in:
parent
a26469894e
commit
822e74fa06
37
NEXT.md
37
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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user