diff --git a/jolt-core/clojure/core/20-coll.clj b/jolt-core/clojure/core/20-coll.clj index caafba9..bba5d47 100644 --- a/jolt-core/clojure/core/20-coll.clj +++ b/jolt-core/clojure/core/20-coll.clj @@ -732,3 +732,31 @@ ([x y & more] (reduce min (min x y) more))) (defn reverse [coll] (reduce conj (list) coll)) + +;; --- Phase 2 leaf batch 3 (jolt-ded) ----------------------------------------- + +;; An empty coll of the same category; sorted colls keep their comparator (the +;; value's own :empty op). Strings and scalars are nil, as in Clojure; a lazy +;; seq empties to () (the old kernel fn returned a host table for it). +(defn empty [coll] + (cond + (nil? coll) nil + (sorted? coll) ((get (jolt.host/ref-get coll :ops) :empty) coll) + (map? coll) {} + (set? coll) #{} + (vector? coll) [] + (coll? coll) () + :else nil)) + +(defn assoc-in [m [k & ks] v] + (if ks + (assoc m k (assoc-in (get m k) ks v)) + (assoc m k v))) + +(defn update-in [m ks f & args] + (let [up (fn up [m ks f args] + (let [[k & ks] ks] + (if ks + (assoc m k (up (get m k) ks f args)) + (assoc m k (apply f (get m k) args)))))] + (up m ks f args))) diff --git a/jolt-core/clojure/core/40-lazy.clj b/jolt-core/clojure/core/40-lazy.clj index 655e4be..aa1a904 100644 --- a/jolt-core/clojure/core/40-lazy.clj +++ b/jolt-core/clojure/core/40-lazy.clj @@ -138,3 +138,40 @@ (when (seq s) (cons (take n s) (go (nthrest s step))))))] (go coll)))) + +;; --- Phase 2 leaf batch 3 (jolt-ded): canonical lazy + transducer arities ---- + +(defn interpose + ([sep] + (fn [rf] + (let [started (volatile! false)] + (fn + ([] (rf)) + ([result] (rf result)) + ([result input] + (if (deref started) + (let [sepr (rf result sep)] + (if (reduced? sepr) + sepr + (rf sepr input))) + (do (vreset! started true) + (rf result input)))))))) + ([sep coll] + (drop 1 (interleave (repeat sep) coll)))) + +(defn take-nth + ([n] + (fn [rf] + (let [iv (volatile! -1)] + (fn + ([] (rf)) + ([result] (rf result)) + ([result input] + (let [i (vswap! iv inc)] + (if (zero? (rem i n)) + (rf result input) + result))))))) + ([n coll] + (lazy-seq + (when-let [s (seq coll)] + (cons (first s) (take-nth n (drop n s))))))) diff --git a/src/jolt/core.janet b/src/jolt/core.janet index 2d2170f..44669e5 100644 --- a/src/jolt/core.janet +++ b/src/jolt/core.janet @@ -2251,19 +2251,10 @@ (defn- ks-rest [ks] (if (tuple? ks) (tuple/slice ks 1) (array/slice ks 1))) -(defn core-assoc-in [m ks v] - (let [ks (vview ks) k (in ks 0)] - (if (<= (length ks) 1) - (core-assoc m k v) - (let [sub (core-get m k)] - (core-assoc m k (core-assoc-in (if (nil? sub) {} sub) (ks-rest ks) v)))))) +# assoc-in / update-in now live in the Clojure collection tier (canonical +# recursive ports). + -(defn core-update-in [m ks f & args] - (let [ks (vview ks) k (in ks 0)] - (if (<= (length ks) 1) - (core-assoc m k (apply f (core-get m k) args)) - (let [sub (core-get m k)] - (core-assoc m k (apply core-update-in (if (nil? sub) {} sub) (ks-rest ks) f args)))))) # fnil now lives in the Clojure collection tier (core/20-coll.clj), with # Clojure's canonical 2/3/4-arity (patch the first 1-3 args only). @@ -2329,61 +2320,21 @@ {:jolt/type :symbol :ns ns :name nm}) (error "symbol expects 1 or 2 args"))) -(defn- td-take-nth [n] - (fn [rf] - (var i 0) - (fn [& a] (case (length a) 0 (rf) 1 (rf (a 0)) - (let [keep (= 0 (mod i n))] (++ i) - (if keep (rf (a 0) (a 1)) (a 0))))))) -(defn core-take-nth [n & rest] - (if (= 0 (length rest)) (td-take-nth n) - (let [coll (in rest 0)] - # Option A: always lazy. - (defn tstep [c] - (fn [] - (if (seq-done? c) nil - (let [drop-n (lazy-from (core-drop n c))] - (if (seq-done? drop-n) @[(core-first c) nil] - @[(core-first c) (tstep drop-n)]))))) - (make-lazy-seq (tstep (lazy-from coll)))))) +# (take-nth's transducer arity lives in the overlay now.) + # filterv now lives in the Clojure collection tier (core/20-coll.clj). # mapv lives in the Clojure kernel tier — core/00-kernel.clj. -(defn- td-interpose [sep] - (fn [rf] - (var started false) - (fn [& a] (case (length a) 0 (rf) 1 (rf (a 0)) - (if started (rf (rf (a 0) sep) (a 1)) - (do (set started true) (rf (a 0) (a 1)))))))) -(defn core-interpose [sep & rest] - (if (= 0 (length rest)) (td-interpose sep) - (let [coll (in rest 0)] - # Option A: always lazy. - (defn istep [c need-sep] - (fn [] - (if (seq-done? c) nil - (if need-sep - @[sep (istep c false)] - @[(core-first c) (istep (core-rest c) true)])))) - (make-lazy-seq (istep (lazy-from coll) false))))) +# (interpose's transducer arity lives in the overlay now.) +# interpose / take-nth now live in the Clojure lazy tier (core/40-lazy.clj), +# with the canonical transducer arities. # keep now lives in the Clojure lazy tier (core/40-lazy.clj). -(defn core-empty [coll] - (cond - # an empty sorted coll of the same kind, KEEPING the comparator (Clojure) - (core-sorted? coll) ((sorted-op coll :empty) coll) - (phm? coll) (make-phm) - (set? coll) (make-phs) - (plist? coll) EMPTY-PLIST - (pvec? coll) (make-vec @[]) - (struct? coll) (struct) - (tuple? coll) (make-vec @[]) - (array? coll) @[] - (table? coll) @{} - nil)) +# empty now lives in the Clojure collection tier (core/20-coll.clj); a lazy +# seq empties to () there (this fn returned a host table for it). # not-empty now lives in the Clojure collection tier (core/20-coll.clj). @@ -2840,7 +2791,6 @@ "newline" core-newline "current-time-ms" core-current-time-ms "parse-uuid" core-parse-uuid - "interpose" core-interpose "mapcat" core-mapcat "transduce" core-transduce "sequence" core-sequence @@ -2851,8 +2801,6 @@ "namespace" core-namespace "reduced" core-reduced "reduced?" core-reduced? - "take-nth" core-take-nth - "empty" core-empty "rseq" core-rseq "shuffle" core-shuffle "ifn?" core-ifn? @@ -3030,8 +2978,6 @@ "ThreadLocal" core-ThreadLocal "IllegalStateException" core-IllegalStateException "resolve" core-resolve - "update-in" core-update-in - "assoc-in" core-assoc-in "copy-core-var" core-copy-core-var "copy-var" core-copy-var "macrofy" core-macrofy diff --git a/test/spec/functions-spec.janet b/test/spec/functions-spec.janet index b2d1689..99a50f7 100644 --- a/test/spec/functions-spec.janet +++ b/test/spec/functions-spec.janet @@ -102,3 +102,35 @@ ["reverse" "(quote (3 2 1))" "(reverse [1 2 3])"] ["reverse empty" "()" "(reverse nil)"] ["conj nil onto map" "{:a 1}" "(conj {:a 1} nil)"]) + +# Phase 2 leaf batch 3 (jolt-ded): empty/assoc-in/update-in (20-coll) and +# interpose/take-nth (40-lazy, with canonical transducer arities). keys/vals/ +# empty? are expander-coupled (00-syntax macros call them) and stay in the +# seed until the fast macro-expansion path lands. +(defspec "clojure.core / leaf batch 3" + ["empty vector" "[]" "(empty [1 2])"] + ["empty list" "()" "(empty (list 1))"] + ["empty map" "{}" "(empty {:a 1})"] + ["empty set" "#{}" "(empty #{1})"] + ["empty nil" "nil" "(empty nil)"] + ["empty string" "nil" "(empty \"abc\")"] + ["empty lazy is ()" "()" "(empty (map inc [1 2]))"] + ["empty sorted keeps cmp" "[3 1]" "(vec (seq (into (empty (sorted-set-by > 1 2)) [1 3])))"] + ["assoc-in" "{:a {:b 1}}" "(assoc-in {} [:a :b] 1)"] + ["assoc-in deep" "{:a {:b {:c 2}}}" "(assoc-in {:a {:b {:c 1}}} [:a :b :c] 2)"] + ["assoc-in keeps siblings" "{:a {:b 1 :c 2}}" "(assoc-in {:a {:b 1}} [:a :c] 2)"] + ["assoc-in vector idx" "[1 9]" "(assoc-in [1 2] [1] 9)"] + ["assoc-in nested vec" "[{:a 9}]" "(assoc-in [{:a 1}] [0 :a] 9)"] + ["update-in" "{:a {:b 2}}" "(update-in {:a {:b 1}} [:a :b] inc)"] + ["update-in extra args" "{:a {:b 111}}" "(update-in {:a {:b 1}} [:a :b] + 10 100)"] + ["update-in fnil" "{:a {:b 1}}" "(update-in {} [:a :b] (fnil inc 0))"] + ["update-in single key" "{:a 2}" "(update-in {:a 1} [:a] inc)"] + ["interpose" "(quote (1 :s 2 :s 3))" "(interpose :s [1 2 3])"] + ["interpose empty" "()" "(interpose :s [])"] + ["interpose one" "(quote (1))" "(interpose :s [1])"] + ["interpose is lazy" "(quote (0 :s 1))" "(take 3 (interpose :s (range)))"] + ["interpose xform" "[\"a\" \",\" \"b\"]" "(vec (sequence (interpose \",\") [\"a\" \"b\"]))"] + ["take-nth" "(quote (1 3 5))" "(take-nth 2 [1 2 3 4 5 6])"] + ["take-nth lazy" "(quote (0 3 6))" "(take 3 (take-nth 3 (range)))"] + ["take-nth xform" "[1 3 5]" "(vec (sequence (take-nth 2) [1 2 3 4 5 6]))"] + ["take-nth into" "[1 4]" "(into [] (take-nth 3) [1 2 3 4 5])"])