feat: persistent singly-linked lists with O(1) conj/cons prepend
Round 3 of the persistent-collections work. Lists were immutable Janet arrays, so conj/cons-prepend was an O(n) copy (O(n^2) to build a list) — a large perf gap vs Clojure's PersistentList. Add src/jolt/plist.janet: an immutable cons-cell list (first/rest/count), same algorithm as Clojure/CLJS/jank PersistentList. conj/cons onto a list now creates an O(1) node that shares the existing list as its tail (no copy), with a cached O(1) count. Repeated conj is O(n) total instead of O(n^2). Hooked plist through first/rest/next/seq/count/peek/pop/nth/empty/empty?, the predicates (list?/seq?/coll?/sequential?), realize-for-iteration, =, coll->cells (concat/lazy), both printers, destructuring, and instance? tags. (list ...) and quoted lists stay arrays; only conj/cons introduce plist nodes, so the surface and risk stay small. Verified: reduce-conj of 200k elements runs in ~0.4s (was effectively O(n^2)). conformance 206/206, features 78/78 (+7 list regressions), jank 120 (+1).
This commit is contained in:
parent
e43f39bc36
commit
a0c9696900
9 changed files with 142 additions and 22 deletions
|
|
@ -75,7 +75,7 @@ Jolt targets Clojure semantics but runs on Janet, not the JVM. The notable diver
|
|||
|
||||
- **Host platform.** No JVM and no Java interop — `import`, `gen-class`, `proxy` of Java classes, and `java.*` are unavailable. `instance?` recognizes a small set of built-in types (`clojure.lang.Atom`, `Number`, `String`, …).
|
||||
- **Numbers.** Janet integers and doubles only — no bignums, ratios, or `BigDecimal`. `(/ 1 3)` is `0.3333…`, large products lose precision, and there are no auto-promoting `+'`/`*'`. `quot`/`rem`/`mod` follow Clojure's sign rules. `bigint`, `rational?`, and `class` are not provided.
|
||||
- **Collections.** By default Jolt uses immutable persistent data structures: vectors are 32-way branching tries (structural-sharing persistent vectors with O(log₃₂ n) `conj`/`assoc`/`nth`), lists are immutable, and maps/sets are persistent hash structures. Value equality and sequence operations are Clojure-compatible, but hash-map/hash-set iteration order is unspecified and differs from Clojure — use `sorted-map`/`sorted-set` when order matters.
|
||||
- **Collections.** By default Jolt uses immutable persistent data structures: vectors are 32-way branching tries (structural-sharing persistent vectors with O(log₃₂ n) `conj`/`assoc`/`nth`), lists are persistent singly-linked cons cells (O(1) `conj`/`cons` prepend with structural sharing), and maps/sets are persistent hash structures. Value equality and sequence operations are Clojure-compatible, but hash-map/hash-set iteration order is unspecified and differs from Clojure — use `sorted-map`/`sorted-set` when order matters.
|
||||
- **Mutable build mode.** Jolt can be compiled to use fast Janet-native *mutable* collections instead, via a build-time flag: `JOLT_MUTABLE=1 jpm build` (default `jpm build` is immutable). In mutable mode vectors and lists share one mutable array representation (so `conj` mutates in place and appends, and `vector?`/`list?` no longer distinguish them) — a performance/looseness trade-off. The default immutable build has full Clojure value semantics.
|
||||
- **Concurrency / STM.** Single-threaded. No refs, `dosync`, agents, or `send`; `locking` evaluates its body without real locking. Atoms, volatiles, and delays are supported.
|
||||
- **Regex.** Compiled to Janet's PEG engine (Janet has no regex). Supported: capturing groups (`[whole g1 …]`), greedy and lazy quantifiers with backtracking, `(?:…)`, lookahead `(?=…)`/`(?!…)`, alternation, anchors `^ $ \b \B`, character classes, and the `(?i)` flag. Not supported: lookbehind, backreferences (`\1`), and named groups (`(?<name>…)`).
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue