test(spec): collections contract (sequences, vectors, lists, maps, sets)

Add behavioral spec suites for the collection API (~180 cases). Writing them
surfaced and fixed real bugs:
- (count nil) errored; now 0 (Clojure semantics)
- (repeat x) and (repeatedly f) — the infinite 1-arg arities — were unsupported;
  now return lazy infinite seqs
- set equality used deep= (representation-sensitive), so a set of map literals
  could differ from an equal set built differently; now value-based (each
  element value-equal to some element of the other)

Known limitation filed: phm-typed maps (from hash-map) used as set elements /
map keys hash by identity (map *literals* are structs and work).

jpm test green.
This commit is contained in:
Yogthos 2026-06-05 00:08:37 -04:00
parent 16428179fa
commit b353d625f1
7 changed files with 303 additions and 15 deletions

View file

@ -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