feat: clojure.core.async on Janet fibers (Phase 1 — API layer)
Janet fibers are stackful coroutines, so a go block is just its body run in a fiber that parks on channel ops by yielding to the event loop — the interpreter call stack rides along, no CPS/state-machine transform. So <!/>! work anywhere (inside try, nested fns, loops), unlike Clojure's go macro. src/jolt/async.janet implements chan/chan?/close!/<!/>!/<!!/>!!/go/go-loop/ thread/alts!/timeout/put!/take! over ev/ channels and fibers, installed as the clojure.core.async namespace (pre-populated in init, so require finds it). A channel is a pair of ev/chans (:ch values + :done close-signal); a take is (ev/select :ch :done), which drains buffered values before the close signal — giving Clojure's drain-then-nil semantics without the buffer loss of ev/chan-close, and with no leaked fibers (close! just closes :done). Single-threaded cooperative scheduling: <! (park) and <!! (block) coincide. Dynamic-var conveyance (Phase 2) and channel transducers (Phase 3) are TODO. spec: test/spec/core-async-spec (16 cases — go/channels, buffering+close, go-loop pipelines, alts!/timeout, parking inside try/nested-fn). jpm test green.
This commit is contained in:
parent
858c7fed14
commit
7d1e1f42e1
7 changed files with 212 additions and 0 deletions
|
|
@ -78,6 +78,7 @@ Jolt targets Clojure semantics but runs on Janet, not the JVM. The notable diver
|
|||
- **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.
|
||||
- **core.async.** `clojure.core.async` runs on Janet fibers and channels (`chan`, `go`, `go-loop`, `<!`/`>!`/`<!!`/`>!!`, `close!`, `alts!`, `timeout`, `put!`/`take!`). Because Janet fibers are stackful coroutines, a `go` block is just its body run in a fiber — no CPS/state-machine rewrite — so `<!`/`>!` work *anywhere*, including inside `try`, nested `fn`s, and loops (positions Clojure's `go` macro forbids). Go blocks are cooperatively scheduled on one OS thread, so parking (`<!`) and blocking (`<!!`) coincide; `thread` runs cooperatively too. Dynamic-var binding conveyance and channel transducers are not yet implemented.
|
||||
- **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>…)`).
|
||||
- **Arrays.** Java-style arrays map onto Janet's native types: `byte-array` is a Janet buffer (contiguous, C-backed); `object-array`/`int-array`/`double-array`/etc. are Janet arrays. `aget`/`aset`/`alength`/`aclone` work over both.
|
||||
- **Transients.** `transient`/`conj!`/`assoc!`/`dissoc!`/`disj!`/`pop!`/`persistent!` are real mutable scratch collections backed by Janet's native arrays and tables (vectors → arrays, maps/sets → tables), so building a collection with them avoids the per-step copying of the persistent path (notably for maps/sets). `persistent!` freezes back to a persistent value.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue