diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index ff69f49..721c067 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -12,6 +12,7 @@ {"_type":"issue","id":"jolt-40n","title":"HIGH: update/update-in/assoc-in fail on map literals (struct)","status":"closed","priority":2,"issue_type":"bug","owner":"yogthos@gmail.com","created_at":"2026-06-04T18:09:56Z","created_by":"Yogthos","updated_at":"2026-06-04T18:40:42Z","closed_at":"2026-06-04T18:40:42Z","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"jolt-ytt","title":"HIGH: str semantics — (str nil)='nil' should be '', (str :b)='b' should be ':b'","status":"closed","priority":2,"issue_type":"bug","owner":"yogthos@gmail.com","created_at":"2026-06-04T18:09:56Z","created_by":"Yogthos","updated_at":"2026-06-04T18:33:45Z","closed_at":"2026-06-04T18:33:45Z","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"jolt-9sb","title":"HIGH: destructuring gaps (nested seq, :strs, nested map, fn-param)","status":"closed","priority":2,"issue_type":"bug","owner":"yogthos@gmail.com","created_at":"2026-06-04T18:09:55Z","created_by":"Yogthos","updated_at":"2026-06-04T18:31:24Z","closed_at":"2026-06-04T18:31:24Z","dependency_count":0,"dependent_count":0,"comment_count":0} +{"_type":"issue","id":"jolt-do7","title":"phm-typed maps as set elements / map keys hash by identity","description":"A map produced via hash-map/assoc (a Janet table/phm) used as a set element or map key hashes by identity, so #{{:a 1}} (struct elem) != #{(hash-map :a 1)} (phm elem) and such elements aren't retrievable by value. Map literals (structs) work. Needs value-based hashing for phm keys in PHM/PHS. Edge case; spec covers literal-map cases.","status":"open","priority":3,"issue_type":"bug","owner":"yogthos@gmail.com","created_at":"2026-06-05T04:07:50Z","created_by":"Yogthos","updated_at":"2026-06-05T04:07:50Z","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"jolt-7dy","title":"MED: compiler path lacks IFn dispatch (collections-as-fn) — only interpreter fixed","status":"closed","priority":3,"issue_type":"bug","owner":"yogthos@gmail.com","created_at":"2026-06-04T18:10:00Z","created_by":"Yogthos","updated_at":"2026-06-04T19:56:36Z","closed_at":"2026-06-04T19:56:36Z","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"jolt-90c","title":"MED: filter/take-while not lazy — infinite-seq (take N (filter p (range))) loops","status":"closed","priority":3,"issue_type":"bug","owner":"yogthos@gmail.com","created_at":"2026-06-04T18:10:00Z","created_by":"Yogthos","updated_at":"2026-06-04T18:56:26Z","closed_at":"2026-06-04T18:56:26Z","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"jolt-7n5","title":"MED: REPL display — def/defn should print #'ns/name; lazy-seq printer realizes only head ((0 \u003cfunction\u003e))","status":"closed","priority":3,"issue_type":"bug","owner":"yogthos@gmail.com","created_at":"2026-06-04T18:09:59Z","created_by":"Yogthos","updated_at":"2026-06-04T18:55:11Z","closed_at":"2026-06-04T18:55:11Z","dependency_count":0,"dependent_count":0,"comment_count":0} diff --git a/src/jolt/core.janet b/src/jolt/core.janet index 843f952..4078ef7 100644 --- a/src/jolt/core.janet +++ b/src/jolt/core.janet @@ -201,7 +201,16 @@ (or sa sb) false # sets (or (set? a) (set? b)) - (if (and (set? a) (set? b)) (deep= (phs-to-struct a) (phs-to-struct b)) false) + # value-based: same size and every element of a is value-equal to some + # element of b (so #{ {:a 1} } equals #{ (hash-map :a 1) } regardless of + # the elements' underlying representations) + (if (and (set? a) (set? b) (= (a :cnt) (b :cnt))) + (let [eb (phs-seq b)] + (var ok true) + (each x (phs-seq a) + (unless (some (fn [y] (jolt-equal? x y)) eb) (set ok false))) + ok) + false) # maps: compare key/value pairs recursively, order-independent true (let [pa (eq-map-pairs a) pb (eq-map-pairs b)] @@ -388,6 +397,7 @@ (defn core-count [coll] (cond + (nil? coll) 0 (core-sorted-map? coll) (length (keys (coll :map))) (core-sorted-set? coll) (length (coll :items)) (lazy-seq? coll) (ls-count coll) @@ -1125,26 +1135,30 @@ (+= i step)) (tuple/slice (tuple ;result)))))) -(def core-repeat (fn [n x] - (var result @[]) - (var i 0) - (while (< i n) - (array/push result x) - (++ i)) - result)) +(defn core-repeat + "(repeat x) -> infinite lazy seq of x; (repeat n x) -> n copies of x." + [a & rest] + (if (= 0 (length rest)) + (do (defn rstep [] (fn [] @[a (rstep)])) (make-lazy-seq (rstep))) + (let [n a x (in rest 0)] + (var result @[]) (var i 0) + (while (< i n) (array/push result x) (++ i)) + result))) (defn core-iterate [f x] "Lazy infinite sequence x, (f x), (f (f x)), ..." (defn istep [v] (fn [] @[v (istep (f v))])) (make-lazy-seq (istep x))) -(defn core-repeatedly [n f] - (var result @[]) - (var i 0) - (while (< i n) - (array/push result (f)) - (++ i)) - result) +(defn core-repeatedly + "(repeatedly f) -> infinite lazy seq of (f) calls; (repeatedly n f) -> n calls." + [a & rest] + (if (= 0 (length rest)) + (do (defn rstep [] (fn [] @[(a) (rstep)])) (make-lazy-seq (rstep))) + (let [n a f (in rest 0)] + (var result @[]) (var i 0) + (while (< i n) (array/push result (f)) (++ i)) + result))) # ============================================================ # Higher-order functions diff --git a/test/spec/lists-spec.janet b/test/spec/lists-spec.janet new file mode 100644 index 0000000..7a35f70 --- /dev/null +++ b/test/spec/lists-spec.janet @@ -0,0 +1,28 @@ +# Specification: lists (persistent singly-linked). +(use ../support/harness) + +(defspec "list / construct & predicate" + ["list" "[1 2 3]" "(list 1 2 3)"] + ["empty list" "[]" "(list)"] + ["quoted list" "[1 2 3]" "(quote (1 2 3))"] + ["list? true" "true" "(list? (list 1 2))"] + ["list? on conj result" "true" "(list? (conj (list 1) 0))"] + ["count" "3" "(count (list 1 2 3))"] + ["empty? true" "true" "(empty? (list))"] + ["list = vector elts" "true" "(= (list 1 2 3) [1 2 3])"]) + +(defspec "list / access & update" + ["first" "1" "(first (list 1 2 3))"] + ["rest" "[2 3]" "(rest (list 1 2 3))"] + ["peek is first" "1" "(peek (list 1 2 3))"] + ["pop drops first" "[2 3]" "(pop (list 1 2 3))"] + ["conj prepends" "[0 1 2]" "(conj (list 1 2) 0)"] + ["conj many prepends" "[4 3 1 2]" "(conj (list 1 2) 3 4)"] + ["cons prepends" "[0 1 2]" "(cons 0 (list 1 2))"] + ["nth" "20" "(nth (list 10 20 30) 1)"]) + +(defspec "list / immutability & performance" + ["conj does not mutate" "true" "(let [l (list 1 2 3) m (conj l 0)] (and (= l [1 2 3]) (= m [0 1 2 3])))"] + ["reduce conj builds" "[2 1 0]" "(reduce conj (list) (range 3))"] + ["O(1) conj at scale" "200000" "(count (reduce conj (list) (range 200000)))"] + ["scale head correct" "199999" "(first (reduce conj (list) (range 200000)))"]) diff --git a/test/spec/maps-spec.janet b/test/spec/maps-spec.janet new file mode 100644 index 0000000..99ad019 --- /dev/null +++ b/test/spec/maps-spec.janet @@ -0,0 +1,56 @@ +# Specification: maps (associative). +(use ../support/harness) + +(defspec "map / construct & predicate" + ["literal" "{:a 1}" "{:a 1}"] + ["hash-map" "{:a 1, :b 2}" "(hash-map :a 1 :b 2)"] + ["empty" "{}" "{}"] + ["map? true" "true" "(map? {:a 1})"] + ["map? false on vector" "false" "(map? [1 2])"] + ["count" "2" "(count {:a 1 :b 2})"] + ["empty? true" "true" "(empty? {})"] + ["equality order-indep" "true" "(= {:a 1 :b 2} {:b 2 :a 1})"]) + +(defspec "map / access" + ["get" "1" "(get {:a 1} :a)"] + ["get missing nil" "nil" "(get {:a 1} :z)"] + ["get default" ":x" "(get {:a 1} :z :x)"] + ["keyword as fn" "1" "(:a {:a 1})"] + ["keyword fn default" ":x" "(:z {:a 1} :x)"] + ["map as fn" "1" "({:a 1} :a)"] + ["get-in" "2" "(get-in {:a {:b 2}} [:a :b])"] + ["get-in missing" "nil" "(get-in {:a {}} [:a :b])"] + ["contains? key" "true" "(contains? {:a 1} :a)"] + ["contains? missing" "false" "(contains? {:a 1} :z)"] + ["find returns entry" "[:a 1]" "(find {:a 1} :a)"] + ["keys" "true" "(= #{:a :b} (set (keys {:a 1 :b 2})))"] + ["vals" "true" "(= #{1 2} (set (vals {:a 1 :b 2})))"]) + +(defspec "map / update" + ["assoc adds" "{:a 1, :b 2}" "(assoc {:a 1} :b 2)"] + ["assoc overwrites" "{:a 9}" "(assoc {:a 1} :a 9)"] + ["assoc many" "{:a 1, :b 2}" "(assoc {} :a 1 :b 2)"] + ["dissoc" "{:a 1}" "(dissoc {:a 1 :b 2} :b)"] + ["dissoc many" "{:a 1}" "(dissoc {:a 1 :b 2 :c 3} :b :c)"] + ["merge" "{:a 1, :b 2}" "(merge {:a 1} {:b 2})"] + ["merge overwrites" "{:a 2}" "(merge {:a 1} {:a 2})"] + ["merge-with" "{:a 3}" "(merge-with + {:a 1} {:a 2})"] + ["update" "{:a 2}" "(update {:a 1} :a inc)"] + ["update missing w/ fnil" "{:a 1}" "(update {} :a (fnil inc 0))"] + ["update-in" "{:a {:b 2}}" "(update-in {:a {:b 1}} [:a :b] inc)"] + ["assoc-in" "{:a {:b 1}}" "(assoc-in {} [:a :b] 1)"] + ["select-keys" "{:a 1}" "(select-keys {:a 1 :b 2} [:a])"] + ["into onto map" "{:a 1, :b 2}" "(into {:a 1} [[:b 2]])"] + ["zipmap" "{:a 1, :b 2}" "(zipmap [:a :b] [1 2])"]) + +(defspec "map / iteration & entries" + ["map over entries" "true" "(= #{1 2} (set (map val {:a 1 :b 2})))"] + ["map keys" "true" "(= #{:a :b} (set (map key {:a 1 :b 2})))"] + ["reduce over entries" "6" "(reduce (fn [a e] (+ a (val e))) 0 {:a 1 :b 2 :c 3})"] + ["reduce-kv" "6" "(reduce-kv (fn [a k v] (+ a v)) 0 {:a 1 :b 2 :c 3})"] + ["destructure entry" "true" "(= [[:a 2]] (into [] (map (fn [[k v]] [k (inc v)]) {:a 1})))"] + ["first of map is entry" "true" "(let [e (first {:a 1})] (and (= (key e) :a) (= (val e) 1)))"] + ["map-entry?" "true" "(map-entry? (first {:a 1}))"] + ["count of nil map" "0" "(count nil)"] + ["get from nil" "nil" "(get nil :a)"] + ["immutability" "true" "(let [m {:a 1} n (assoc m :b 2)] (and (= m {:a 1}) (= n {:a 1 :b 2})))"]) diff --git a/test/spec/sequences-spec.janet b/test/spec/sequences-spec.janet new file mode 100644 index 0000000..ce09752 --- /dev/null +++ b/test/spec/sequences-spec.janet @@ -0,0 +1,105 @@ +# Specification: the sequence abstraction (clojure.core). +# Sequential expecteds use vector literals — Jolt's `=` treats vectors and lists +# with the same elements as equal, so [2 3 4] matches a (2 3 4) seq result. +(use ../support/harness) + +(defspec "seq / access" + ["first of vector" "1" "(first [1 2 3])"] + ["first of list" "1" "(first (list 1 2 3))"] + ["first of empty is nil" "nil" "(first [])"] + ["first of nil is nil" "nil" "(first nil)"] + ["second" "2" "(second [1 2 3])"] + ["last" "3" "(last [1 2 3])"] + ["rest of vector" "[2 3]" "(rest [1 2 3])"] + ["rest of single" "[]" "(rest [1])"] + ["rest of empty" "[]" "(rest [])"] + ["next of single is nil" "nil" "(next [1])"] + ["next of empty is nil" "nil" "(next [])"] + ["nth" "30" "(nth [10 20 30] 2)"] + ["nth with default" "99" "(nth [10] 5 99)"] + ["nth out of range" :throws "(nth [10] 5)"] + ["ffirst" "1" "(ffirst [[1 2] [3 4]])"] + ["fnext" "2" "(fnext [1 2 3])"] + ["nnext" "[3 4]" "(nnext [1 2 3 4])"]) + +(defspec "seq / construction" + ["cons onto list" "[0 1 2]" "(cons 0 (list 1 2))"] + ["cons onto vector" "[0 1 2]" "(cons 0 [1 2])"] + ["cons onto nil" "[0]" "(cons 0 nil)"] + ["conj vector appends" "[1 2 3]" "(conj [1 2] 3)"] + ["conj list prepends" "[0 1 2]" "(conj (list 1 2) 0)"] + ["conj multiple on vec" "[1 2 3 4]" "(conj [1 2] 3 4)"] + ["conj multiple on list" "[4 3 1 2]" "(conj (list 1 2) 3 4)"] + ["seq of empty is nil" "nil" "(seq [])"] + ["seq of nil is nil" "nil" "(seq nil)"] + ["seq of string" "[\\a \\b]" "(seq \"ab\")"] + ["empty?" "true" "(empty? [])"] + ["not empty?" "false" "(empty? [1])"] + ["count" "3" "(count [1 2 3])"] + ["count of nil" "0" "(count nil)"] + ["count of string" "3" "(count \"abc\")"]) + +(defspec "seq / map filter reduce" + ["map" "[2 3 4]" "(map inc [1 2 3])"] + ["map two colls" "[5 7 9]" "(map + [1 2 3] [4 5 6])"] + ["map stops at shortest" "[5 7]" "(map + [1 2] [4 5 6])"] + ["map-indexed" "[[0 :a] [1 :b]]" "(map-indexed vector [:a :b])"] + ["mapv" "[2 3 4]" "(mapv inc [1 2 3])"] + ["filter" "[2 4]" "(filter even? [1 2 3 4])"] + ["filterv" "[2 4]" "(filterv even? [1 2 3 4])"] + ["remove" "[1 3]" "(remove even? [1 2 3 4])"] + ["reduce" "10" "(reduce + [1 2 3 4])"] + ["reduce with init" "20" "(reduce + 10 [1 2 3 4])"] + ["reduce empty with init" "0" "(reduce + 0 [])"] + ["reduce single no init" "5" "(reduce + [5])"] + ["reduced short-circuits" "3" "(reduce (fn [a x] (if (> a 2) (reduced a) (+ a x))) 0 [1 2 3 4 5])"] + ["reduce-kv" "6" "(reduce-kv (fn [a k v] (+ a v)) 0 {:a 1 :b 2 :c 3})"] + ["reductions" "[1 3 6]" "(reductions + [1 2 3])"] + ["mapcat" "[1 1 2 2]" "(mapcat (fn [x] [x x]) [1 2])"] + ["keep" "[1 3]" "(keep (fn [x] (if (odd? x) x nil)) [1 2 3 4])"] + ["some truthy" "true" "(some even? [1 2 3])"] + ["some nil" "nil" "(some even? [1 3 5])"] + ["every? true" "true" "(every? pos? [1 2 3])"] + ["every? false" "false" "(every? pos? [1 -2 3])"]) + +(defspec "seq / take drop slice" + ["take" "[1 2 3]" "(take 3 [1 2 3 4 5])"] + ["take more than size" "[1 2]" "(take 5 [1 2])"] + ["drop" "[4 5]" "(drop 3 [1 2 3 4 5])"] + ["take-while" "[1 2]" "(take-while (fn [x] (< x 3)) [1 2 3 1])"] + ["drop-while" "[3 1]" "(drop-while (fn [x] (< x 3)) [1 2 3 1])"] + ["take-last" "[4 5]" "(take-last 2 [1 2 3 4 5])"] + ["drop-last" "[1 2 3]" "(drop-last [1 2 3 4])"] + ["take-nth" "[1 3 5]" "(take-nth 2 [1 2 3 4 5])"] + ["partition" "[[1 2] [3 4]]" "(partition 2 [1 2 3 4 5])"] + ["partition-all" "[[1 2] [3]]" "(partition-all 2 [1 2 3])"] + ["split-at" "[[1 2] [3 4]]" "(split-at 2 [1 2 3 4])"]) + +(defspec "seq / transform" + ["reverse" "[3 2 1]" "(reverse [1 2 3])"] + ["sort" "[1 2 3]" "(sort [3 1 2])"] + ["sort with comparator" "[3 2 1]" "(sort > [1 3 2])"] + ["sort-by" "[[1] [2 2]]" "(sort-by count [[2 2] [1]])"] + ["distinct" "[1 2 3]" "(distinct [1 1 2 3 3])"] + ["dedupe" "[1 2 1]" "(dedupe [1 1 2 1])"] + ["interpose" "[1 0 2 0 3]" "(interpose 0 [1 2 3])"] + ["interleave" "[1 :a 2 :b]" "(interleave [1 2] [:a :b])"] + ["flatten" "[1 2 3 4]" "(flatten [1 [2 [3 [4]]]])"] + ["concat" "[1 2 3 4]" "(concat [1 2] [3 4])"] + ["into vector" "[1 2 3 4]" "(into [1 2] [3 4])"] + ["into list" "[3 2 1]" "(into (list) [1 2 3])"] + ["frequencies" "{1 2, 2 1}" "(frequencies [1 1 2])"] + ["group-by" "{false [1 3], true [2 4]}" "(group-by even? [1 2 3 4])"] + ["zipmap" "{:a 1, :b 2}" "(zipmap [:a :b] [1 2])"] + ["mapcat seqs" "[1 2 3 4]" "(mapcat identity [[1 2] [3 4]])"]) + +(defspec "seq / generators" + ["range n" "[0 1 2 3]" "(range 4)"] + ["range from to" "[2 3 4]" "(range 2 5)"] + ["range with step" "[0 2 4]" "(range 0 6 2)"] + ["take repeat" "[:x :x :x]" "(take 3 (repeat :x))"] + ["repeat n" "[5 5]" "(repeat 2 5)"] + ["take iterate" "[1 2 4 8]" "(take 4 (iterate (fn [x] (* x 2)) 1))"] + ["take cycle" "[1 2 1 2 1]" "(take 5 (cycle [1 2]))"] + ["take repeatedly" "3" "(count (take 3 (repeatedly (fn [] 1))))"] + ["take-last of range" "[8 9]" "(take-last 2 (range 10))"]) diff --git a/test/spec/sets-spec.janet b/test/spec/sets-spec.janet new file mode 100644 index 0000000..587fe58 --- /dev/null +++ b/test/spec/sets-spec.janet @@ -0,0 +1,35 @@ +# Specification: sets, including clojure.set. +(use ../support/harness) + +(defspec "set / construct & predicate" + ["literal" "#{1 2 3}" "#{1 2 3}"] + ["hash-set" "#{1 2 3}" "(hash-set 1 2 3)"] + ["set from vector" "#{1 2 3}" "(set [1 2 3 1])"] + ["empty" "#{}" "#{}"] + ["set? true" "true" "(set? #{1})"] + ["set? false on vector" "false" "(set? [1])"] + ["count dedups" "3" "(count (set [1 1 2 3]))"] + ["equality order-indep" "true" "(= #{1 2 3} #{3 2 1})"]) + +(defspec "set / operations" + ["conj adds" "#{1 2 3}" "(conj #{1 2} 3)"] + ["conj dup no-op" "#{1 2}" "(conj #{1 2} 1)"] + ["disj removes" "#{1 2}" "(disj #{1 2 3} 3)"] + ["disj missing no-op" "#{1 2}" "(disj #{1 2} 9)"] + ["contains?" "true" "(contains? #{1 2} 1)"] + ["contains? missing" "false" "(contains? #{1 2} 9)"] + ["get present" "1" "(get #{1 2} 1)"] + ["get missing nil" "nil" "(get #{1 2} 9)"] + ["set as fn present" "2" "(#{1 2 3} 2)"] + ["set as fn missing" "nil" "(#{1 2 3} 9)"]) + +(defspec "clojure.set" + ["union" "#{1 2 3 4}" "(do (require (quote [clojure.set :as s])) (s/union #{1 2} #{3 4}))"] + ["intersection" "#{2}" "(do (require (quote [clojure.set :as s])) (s/intersection #{1 2} #{2 3}))"] + ["difference" "#{1}" "(do (require (quote [clojure.set :as s])) (s/difference #{1 2} #{2 3}))"] + ["subset? true" "true" "(do (require (quote [clojure.set :as s])) (s/subset? #{1} #{1 2}))"] + ["superset? true" "true" "(do (require (quote [clojure.set :as s])) (s/superset? #{1 2} #{1}))"] + ["select" "#{2 4}" "(do (require (quote [clojure.set :as s])) (s/select even? #{1 2 3 4}))"] + ["join" "#{{:a 1, :b 2, :c 3}}" "(do (require (quote [clojure.set :as s])) (s/join #{{:a 1 :b 2}} #{{:b 2 :c 3}}))"] + ["map-invert" "{1 :a}" "(do (require (quote [clojure.set :as s])) (s/map-invert {:a 1}))"] + ["rename-keys" "{:b 1}" "(do (require (quote [clojure.set :as s])) (s/rename-keys {:a 1} {:a :b}))"]) diff --git a/test/spec/vectors-spec.janet b/test/spec/vectors-spec.janet new file mode 100644 index 0000000..8debf03 --- /dev/null +++ b/test/spec/vectors-spec.janet @@ -0,0 +1,49 @@ +# Specification: vectors (persistent, indexed). +(use ../support/harness) + +(defspec "vector / construct & predicate" + ["literal" "[1 2 3]" "[1 2 3]"] + ["vector" "[1 2 3]" "(vector 1 2 3)"] + ["vector zero args" "[]" "(vector)"] + ["vec from list" "[1 2 3]" "(vec (list 1 2 3))"] + ["vec from range" "[0 1 2]" "(vec (range 3))"] + ["vec of map yields entries" "[[:a 1]]" "(vec {:a 1})"] + ["vector? true" "true" "(vector? [1])"] + ["vector? false on list" "false" "(vector? (list 1))"] + ["vector = list elts" "true" "(= [1 2 3] (list 1 2 3))"]) + +(defspec "vector / access" + ["nth" ":b" "(nth [:a :b :c] 1)"] + ["nth default" ":x" "(nth [:a] 5 :x)"] + ["get by index" ":b" "(get [:a :b] 1)"] + ["get out of range nil" "nil" "(get [:a] 5)"] + ["get default" ":x" "(get [:a] 5 :x)"] + ["first" "1" "(first [1 2 3])"] + ["last" "3" "(last [1 2 3])"] + ["peek is last" "3" "(peek [1 2 3])"] + ["count" "3" "(count [1 2 3])"] + ["contains? index" "true" "(contains? [:a :b] 1)"] + ["contains? past end" "false" "(contains? [:a] 3)"] + ["vector as fn" ":b" "([:a :b :c] 1)"]) + +(defspec "vector / update (persistent)" + ["conj appends" "[1 2 3]" "(conj [1 2] 3)"] + ["conj many" "[1 2 3 4]" "(conj [1 2] 3 4)"] + ["assoc index" "[1 9 3]" "(assoc [1 2 3] 1 9)"] + ["assoc at count appends" "[1 2 3]" "(assoc [1 2] 2 3)"] + ["update" "[1 3 3]" "(update [1 2 3] 1 inc)"] + ["pop drops last" "[1 2]" "(pop [1 2 3])"] + ["subvec start end" "[2 3]" "(subvec [1 2 3 4] 1 3)"] + ["subvec to end" "[3 4]" "(subvec [1 2 3 4] 2)"] + ["mapv" "[2 3 4]" "(mapv inc [1 2 3])"] + ["filterv" "[2 4]" "(filterv even? [1 2 3 4])"]) + +(defspec "vector / immutability & nesting" + ["conj does not mutate" "true" "(let [v [1 2] w (conj v 3)] (and (= v [1 2]) (= w [1 2 3])))"] + ["assoc does not mutate" "true" "(let [v [1 2 3] w (assoc v 0 9)] (and (= v [1 2 3]) (= w [9 2 3])))"] + ["get-in" "2" "(get-in [[1 2] [3 4]] [0 1])"] + ["assoc-in" "[[1 9]]" "(assoc-in [[1 2]] [0 1] 9)"] + ["update-in" "[[1 3]]" "(update-in [[1 2]] [0 1] inc)"] + ["large vector nth" "1500" "(nth (vec (range 2000)) 1500)"] + ["large vector count" "2000" "(count (vec (range 2000)))"] + ["large conj immutable" "true" "(let [v (vec (range 1000)) w (conj v :end)] (and (= 1000 (count v)) (= 1001 (count w))))"])