fix(runtime): force lazy seqs at every eager consumer - #26
Open
BuddhiLW wants to merge 3 commits into
Open
Conversation
clel-map and clel-filter return a lazy seq, but almost nothing forced one. Elisp primitives cannot: `length` measures the four-element struct and `apply` signals wrong-type-argument. So (reduce + (map inc c)) threw, and (count (map inc c)) answered 4 for three elements. The runtime now has one coercion, clel-realize, and every fn that hands a sequence to a raw primitive crosses through it. count, apply, second, butlast, reverse, flatten and remove gained clel- wrappers instead of mapping onto Elisp directly; mappings/lazy-seq-consuming-fns declares the rule and validate-tables! enforces it. Also fixes three defects the execution-rung tests surfaced: - clel-rest returned an unforced thunk as its tail, so every walking loop ran one iteration past the end (frequencies reported a phantom nil, every? answered nil, reduce threw) - distinct/keep/dedupe/interpose dispatch on `&optional coll`, which cannot tell "no collection" from "empty collection", so recursing onto an empty tail returned a transducer mid-sequence - distinct allocated a fresh seen table per step, keeping distant duplicates None of this was visible to the 603 existing tests: they assert on emitted strings, and the emitted string was right the whole time. Adds test/elisp/clojure-elisp-runtime-test.el, which loads the runtime into Emacs and calls it, and runs the elisp suites in CI. 12 of its 13 tests fail against the 0.7.1 runtime.
The header is stamped from the VERSION resource at regen time, so a regen that runs before the bump leaves the shipped runtime naming the previous version.
sync-version writes the VERSION resource but cannot restamp the .el header, which is only written by a regen. Bumping VERSION after a regen therefore ships a runtime naming the previous release, silently. Part of kanban 20260905161109-0e698b23.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes the CLJEL-LAZY-FORCE card (
20260905155515-51248d63).The defect
clel-mapandclel-filterreturn a lazy sequence, which is correct. Almostnothing forced one, and Elisp primitives cannot:
lengthmeasures thefour-element
clel-lazy-seqstruct,applysignalswrong-type-argument.Measured against the shipped 0.7.1 runtime:
(reduce + (map inc c))wrong-type-argument(apply + (map inc c))wrong-type-argument(count (map inc c))(last (map inc c))(into [] (map inc c))(2 3 4)The two silent answers are the dangerous half.
Why no test caught it
All 603 tests assert on emitted strings, and the emitted string was correct
the whole time. The defect lives one layer down, in what that string does when
Emacs runs it. There was no tier that executed the runtime, and the one ERT
suite that existed was behind a
maketarget CI never invoked.The fix
One coercion in the runtime's Promote layer,
clel-realize, and a Boundarystratum: every fn that hands a sequence to a raw Elisp primitive crosses
through it first.
count,apply,second,butlast,reverse,flattenand
removegainedclel-wrappers instead of mapping onto Elisp directly.Rather than fixing N mapping rows by hand, the rule is stated once as data:
mappings/lazy-seq-consuming-fnsdeclares which fns read a possibly-lazysequence, and
validate-tables!rejects any of them pointed at a rawprimitive. A future mapping edit cannot quietly reintroduce this.
Three more defects the execution tier surfaced
clel-restreturned an unforced thunk as its tail, which is truthy, so everywhilewalking withclel-first/clel-restran one iteration past the end.That is why
frequenciesreported a phantom(nil . 1),every?answerednilfor a sequence that satisfies its predicate, andreducethrew.distinct,keep,dedupeandinterposedispatch on&optional coll,which cannot tell "no collection given" from "collection is empty", so
recursing onto an empty tail returned a transducer mid-sequence.
distinctallocated a freshseentable per recursion, so(distinct [1 2 1])returned(1 2 1).Evidence
runtime, and pass against this one
.cljelsource usingreduce/map,count/filter,join/map,last/sort-by,apply,into/distinct,removeandreversecompiled and executed in batch Emacs, all correctCompatibility
Emitted output for
count,apply,second,butlast,reverse,flattenand
removediffers from 0.7.1. Behaviour only becomes more correct, andremoveis now lazy as in Clojure, but recompile rather than mixing 0.7.1output against the 0.7.2 runtime.