Refactor: extract LazySeq from phm.janet into lazyseq.janet (#108)
phm.janet held the PersistentHashMap, the PersistentHashSet, AND the LazySeq primitives — a lazy sequence has nothing to do with hash maps; both were just tagged tables, which is why they shared a file (jolt-bvek). An agent looking for lazy-seq realization would never grep phm.janet. Move the LazySeq section (lazy-seq?/make-lazy-seq/realize-ls/ls-first/ls-rest/ ls-rest-cached/ls-seq/ls-count/lazy-cons) to a new self-contained lazyseq.janet (janet builtins only, no jolt deps). Importers that used the fns through (use ./phm) add (use ./lazyseq); host_interop's one phm/lazy-seq? becomes lazy-seq?. Behaviour unchanged (covered by test/unit/lazy-seq-test.janet + the full gate). phs split is a follow-up. Co-authored-by: Yogthos <yogthos@gmail.com>
This commit is contained in:
parent
62fe11fa0d
commit
7cffe85298
6 changed files with 87 additions and 85 deletions
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
(use ./types)
|
(use ./types)
|
||||||
(use ./phm)
|
(use ./phm)
|
||||||
|
(use ./lazyseq)
|
||||||
(use ./regex)
|
(use ./regex)
|
||||||
(use ./config)
|
(use ./config)
|
||||||
(use ./pv)
|
(use ./pv)
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
(use ./types)
|
(use ./types)
|
||||||
(use ./phm)
|
(use ./phm)
|
||||||
|
(use ./lazyseq)
|
||||||
(use ./pv)
|
(use ./pv)
|
||||||
(use ./plist)
|
(use ./plist)
|
||||||
(use ./config)
|
(use ./config)
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
(use ./pv)
|
(use ./pv)
|
||||||
(use ./plist)
|
(use ./plist)
|
||||||
(use ./types)
|
(use ./types)
|
||||||
|
(use ./lazyseq)
|
||||||
(import ./phm)
|
(import ./phm)
|
||||||
|
|
||||||
(defn- chr [s] (get s 0))
|
(defn- chr [s] (get s 0))
|
||||||
|
|
@ -791,7 +792,7 @@
|
||||||
# clojure.core equivalent. :jolt/ci-none means "not a collection method here".
|
# clojure.core equivalent. :jolt/ci-none means "not a collection method here".
|
||||||
(set-coll-interop!
|
(set-coll-interop!
|
||||||
(fn [target name args]
|
(fn [target name args]
|
||||||
(if-not (or (pvec? target) (phm/phm? target) (plist? target) (phm/lazy-seq? target)
|
(if-not (or (pvec? target) (phm/phm? target) (plist? target) (lazy-seq? target)
|
||||||
(and (table? target) (= :jolt/set (get target :jolt/type)))
|
(and (table? target) (= :jolt/set (get target :jolt/type)))
|
||||||
(shape-rec? target) # map-as-tuple record
|
(shape-rec? target) # map-as-tuple record
|
||||||
(and (struct? target) (nil? (get target :jolt/type)))) # plain map literal
|
(and (struct? target) (nil? (get target :jolt/type)))) # plain map literal
|
||||||
|
|
|
||||||
82
src/jolt/lazyseq.janet
Normal file
82
src/jolt/lazyseq.janet
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
# LazySeq — cell-by-cell lazy sequence (Clojure-compatible)
|
||||||
|
#
|
||||||
|
# Model: a thunk returns nil (empty) or a [first-val, rest-thunk] pair; each
|
||||||
|
# step produces one element + a thunk for the rest. Supports self-referencing
|
||||||
|
# sequences like fib-seq. Self-contained (janet builtins only) — the Clojure
|
||||||
|
# seq layer (core.janet) and the interpreter build on these primitives.
|
||||||
|
#
|
||||||
|
# Extracted from phm.janet (jolt-bvek): a lazy sequence has nothing to do with
|
||||||
|
# hash maps; both were tagged tables, which is why they shared a file.
|
||||||
|
|
||||||
|
(defn lazy-seq?
|
||||||
|
"Check if x is a LazySeq."
|
||||||
|
[x]
|
||||||
|
(and (table? x) (= :jolt/lazy-seq (x :jolt/type))))
|
||||||
|
|
||||||
|
(defn make-lazy-seq [thunk]
|
||||||
|
@{:jolt/type :jolt/lazy-seq :fn thunk :realized false :val nil})
|
||||||
|
|
||||||
|
(defn realize-ls
|
||||||
|
"Force a LazySeq cell. Returns nil (empty) or [first-val, rest-thunk].
|
||||||
|
If the thunk returns another lazy-seq, recursively realize it.
|
||||||
|
Uses :jolt/pending sentinel to detect self-referencing cycles."
|
||||||
|
[ls]
|
||||||
|
(if (get ls :realized)
|
||||||
|
(ls :val)
|
||||||
|
(do
|
||||||
|
(put ls :val :jolt/pending)
|
||||||
|
(put ls :realized true)
|
||||||
|
(let [raw ((ls :fn))
|
||||||
|
v (if (lazy-seq? raw) (realize-ls raw) raw)]
|
||||||
|
(put ls :val v)
|
||||||
|
v))))
|
||||||
|
|
||||||
|
(defn ls-first [ls]
|
||||||
|
(let [cell (realize-ls ls)]
|
||||||
|
(if (or (nil? cell) (= :jolt/pending cell) (= 0 (length cell))) nil (in cell 0))))
|
||||||
|
|
||||||
|
# The memoized rest wrapper for a node whose cell yielded rest-thunk rt.
|
||||||
|
# EVERY walk must go through this (not a fresh make-lazy-seq) or independent
|
||||||
|
# walks re-run the shared thunks and side effects duplicate.
|
||||||
|
(defn ls-rest-cached [ls rt]
|
||||||
|
(or (get ls :rest-ls)
|
||||||
|
(let [w (make-lazy-seq rt)]
|
||||||
|
(put ls :rest-ls w)
|
||||||
|
w)))
|
||||||
|
|
||||||
|
(defn ls-rest [ls]
|
||||||
|
(let [cell (realize-ls ls)]
|
||||||
|
(if (or (nil? cell) (= 0 (length cell))) nil
|
||||||
|
(let [rt (in cell 1)]
|
||||||
|
(if (nil? rt) nil
|
||||||
|
# Memoized wrapper (see ls-rest-cached): a fresh table per call gave
|
||||||
|
# every independent walk its own realization state, so the shared
|
||||||
|
# rest-thunks re-ran — duplicating side effects (a doall'd seq of
|
||||||
|
# futures re-spawned them on the deref walk, serializing pmap).
|
||||||
|
(ls-rest-cached ls rt))))))
|
||||||
|
|
||||||
|
(defn ls-seq [ls]
|
||||||
|
(var result @[])
|
||||||
|
(var cur ls)
|
||||||
|
(while (not (nil? cur))
|
||||||
|
(let [cell (realize-ls cur)]
|
||||||
|
(if (nil? cell) (break))
|
||||||
|
(array/push result (in cell 0))
|
||||||
|
(set cur (ls-rest cur))))
|
||||||
|
(if (= 0 (length result)) nil result))
|
||||||
|
|
||||||
|
(defn ls-count [ls]
|
||||||
|
(var cnt 0)
|
||||||
|
(var cur ls)
|
||||||
|
(while (not (nil? cur))
|
||||||
|
(let [cell (realize-ls cur)]
|
||||||
|
(if (nil? cell) (break))
|
||||||
|
(++ cnt)
|
||||||
|
(set cur (ls-rest cur))))
|
||||||
|
cnt)
|
||||||
|
|
||||||
|
(defn lazy-cons
|
||||||
|
"Returns a LazySeq whose first element is x and whose rest is produced by
|
||||||
|
rest-thunk (a 0-arg function returning nil or a LazySeq)."
|
||||||
|
[x rest-thunk]
|
||||||
|
(make-lazy-seq (fn [] @[x rest-thunk])))
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
(use ./api)
|
(use ./api)
|
||||||
(use ./types)
|
(use ./types)
|
||||||
(use ./phm)
|
(use ./phm)
|
||||||
|
(use ./lazyseq)
|
||||||
(use ./pv)
|
(use ./pv)
|
||||||
(use ./plist)
|
(use ./plist)
|
||||||
(use ./config)
|
(use ./config)
|
||||||
|
|
|
||||||
|
|
@ -174,90 +174,6 @@
|
||||||
(while (< i n) (set m (phm-assoc m (kvs i) (kvs (+ i 1)))) (+= i 2)))
|
(while (< i n) (set m (phm-assoc m (kvs i) (kvs (+ i 1)))) (+= i 2)))
|
||||||
m)
|
m)
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
# LazySeq — cell-by-cell lazy sequence (Clojure-compatible)
|
|
||||||
# ============================================================
|
|
||||||
# Model: thunk returns nil (empty) or [first-val, rest-thunk] pair.
|
|
||||||
# Each step produces one element + thunk for the rest.
|
|
||||||
# Supports self-referencing sequences like fib-seq.
|
|
||||||
|
|
||||||
(defn lazy-seq?
|
|
||||||
"Check if x is a LazySeq."
|
|
||||||
[x]
|
|
||||||
(and (table? x) (= :jolt/lazy-seq (x :jolt/type))))
|
|
||||||
|
|
||||||
(defn make-lazy-seq [thunk]
|
|
||||||
@{:jolt/type :jolt/lazy-seq :fn thunk :realized false :val nil})
|
|
||||||
|
|
||||||
(defn realize-ls
|
|
||||||
"Force a LazySeq cell. Returns nil (empty) or [first-val, rest-thunk].
|
|
||||||
If the thunk returns another lazy-seq, recursively realize it.
|
|
||||||
Uses :jolt/pending sentinel to detect self-referencing cycles."
|
|
||||||
[ls]
|
|
||||||
(if (get ls :realized)
|
|
||||||
(ls :val)
|
|
||||||
(do
|
|
||||||
(put ls :val :jolt/pending)
|
|
||||||
(put ls :realized true)
|
|
||||||
(let [raw ((ls :fn))
|
|
||||||
v (if (lazy-seq? raw) (realize-ls raw) raw)]
|
|
||||||
(put ls :val v)
|
|
||||||
v))))
|
|
||||||
|
|
||||||
(defn ls-first [ls]
|
|
||||||
(let [cell (realize-ls ls)]
|
|
||||||
(if (or (nil? cell) (= :jolt/pending cell) (= 0 (length cell))) nil (in cell 0))))
|
|
||||||
|
|
||||||
# The memoized rest wrapper for a node whose cell yielded rest-thunk rt.
|
|
||||||
# EVERY walk must go through this (not a fresh make-lazy-seq) or independent
|
|
||||||
# walks re-run the shared thunks and side effects duplicate.
|
|
||||||
(defn ls-rest-cached [ls rt]
|
|
||||||
(or (get ls :rest-ls)
|
|
||||||
(let [w (make-lazy-seq rt)]
|
|
||||||
(put ls :rest-ls w)
|
|
||||||
w)))
|
|
||||||
|
|
||||||
(defn ls-rest [ls]
|
|
||||||
(let [cell (realize-ls ls)]
|
|
||||||
(if (or (nil? cell) (= 0 (length cell))) nil
|
|
||||||
(let [rt (in cell 1)]
|
|
||||||
(if (nil? rt) nil
|
|
||||||
# Memoized wrapper (see ls-rest-cached): a fresh table per call gave
|
|
||||||
# every independent walk its own realization state, so the shared
|
|
||||||
# rest-thunks re-ran — duplicating side effects (a doall'd seq of
|
|
||||||
# futures re-spawned them on the deref walk, serializing pmap).
|
|
||||||
(ls-rest-cached ls rt))))))
|
|
||||||
|
|
||||||
(defn ls-seq [ls]
|
|
||||||
(var result @[])
|
|
||||||
(var cur ls)
|
|
||||||
(while (not (nil? cur))
|
|
||||||
(let [cell (realize-ls cur)]
|
|
||||||
(if (nil? cell) (break))
|
|
||||||
(array/push result (in cell 0))
|
|
||||||
(set cur (ls-rest cur))))
|
|
||||||
(if (= 0 (length result)) nil result))
|
|
||||||
|
|
||||||
(defn ls-count [ls]
|
|
||||||
(var cnt 0)
|
|
||||||
(var cur ls)
|
|
||||||
(while (not (nil? cur))
|
|
||||||
(let [cell (realize-ls cur)]
|
|
||||||
(if (nil? cell) (break))
|
|
||||||
(++ cnt)
|
|
||||||
(set cur (ls-rest cur))))
|
|
||||||
cnt)
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
# Lazy combinator — primitive for building lazy sequences
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
(defn lazy-cons
|
|
||||||
"Returns a LazySeq whose first element is x and whose rest is produced
|
|
||||||
by rest-thunk (a 0-arg function returning nil or a LazySeq)."
|
|
||||||
[x rest-thunk]
|
|
||||||
(make-lazy-seq (fn [] @[x rest-thunk])))
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# PersistentHashSet — backed by PersistentHashMap
|
# PersistentHashSet — backed by PersistentHashMap
|
||||||
# ============================================================
|
# ============================================================
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue