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`.
|
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
|
## 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
|
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