Four Clojure wrinkles we are not inheriting, and the rule that replaces each

This commit is contained in:
Joseph Ferano 2026-09-12 22:21:35 +07:00
parent 822e74fa06
commit ea83b2a6b1

28
NEXT.md
View File

@ -590,6 +590,34 @@ 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`.
## Decided: the Clojure patterns we are deliberately not copying
This language borrows Clojure's shape and is **not** trying to be Clojure-compatible, so its known wrinkles are ours
to avoid rather than inherit. Four, with what to do instead.
**1. One argument-order rule, held everywhere.** Clojure's sequence functions take the collection *last*
(`(map f coll)`) and its collection functions take it *first* (`(assoc m k v)`). The split is deliberate there, and it
is why Clojure needs **two** threading macros instead of one. **Our rule: the thing being operated on comes first.**
That is already what the language does — `(at a i)`, `(len xs)`, `(push v x)`, `(as-slice v)` — and `into` follows it
with the source first. Hold it; do not ship two of anything to paper over a split.
**2. A membership test says which thing it tests.** Clojure's `contains?` checks *keys*, so `(contains? [1 2 3] 1)` is
true because index 1 exists — the most-cited confusion in the language, and there is no built-in for "is this value in
this list". Half of this is already right here: `has-key?` on a `Map` is named for what it does. If a value-membership
test is added for sequences, name it for values and never overload one name across both meanings.
**3. A predicate returns a boolean; a search returns what it found.** Clojure's `some` returns the *value*, so
`(some even? [1 2])` is `true` but `(some identity [nil false])` is `nil` — one name doing two jobs. Keep them apart:
a `?` name answers yes or no, a finder answers the thing or nothing, and neither pretends to be the other.
**4. Composition reads in the same direction as threading.** Clojure's `comp` is right-to-left while `->` is
left-to-right, so the two compose mentally in opposite directions. If anything here ever composes operations, it reads
left to right, the way `into` does.
Sources are community consensus rather than a specification; the `contains?` complaint is documented in *Getting
Clojure*. Recorded because these are cheap to honour now and expensive to unpick once a standard library depends on
them.
## 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.**