fix: case composite constants, associative?/reversible?, update non-fn args, nth nil

- case: quote list literals (read as arrays) in constant position so a wrapped
  list ((a b c)) matches by value instead of being evaluated as a call; symbol
  constants already quoted. Vector/map/set constants already worked. case errors
  in the suite drop to 0 (60 pass).
- associative?: true only for vectors (pvec) and maps (phm/struct/sorted-map),
  not lists/tuples-from-seq-fns/lazy-seqs/sets.
- reversible?: true for vectors and sorted-map/sorted-set only.
- update: coerce f via as-fn so (update m k :kw)/(update m k a-set) work; extra
  args already handled.
- nth: (nth nil i)/(nth nil i default) returns nil/default instead of throwing.

clojure-test-suite pass 3649->3678, errors 122->105, clean files 44->46.
associative?/reversible? files now fully clean. spec: predicates + control/case.
jpm test green.
This commit is contained in:
Yogthos 2026-06-05 10:19:43 -04:00
parent 2ccfa675f7
commit bcdace7543
4 changed files with 28 additions and 7 deletions

View file

@ -1023,6 +1023,7 @@
(defn core-nth (defn core-nth
"Return the nth element of a sequential collection." "Return the nth element of a sequential collection."
[coll idx &opt default] [coll idx &opt default]
(if (nil? coll) default # (nth nil i) -> nil / default, never throws
(if (core-transient? coll) (if (core-transient? coll)
(let [a (coll :arr)] (if (and (>= idx 0) (< idx (length a))) (in a idx) default)) (let [a (coll :arr)] (if (and (>= idx 0) (< idx (length a))) (in a idx) default))
(if (plist? coll) (if (plist? coll)
@ -1050,7 +1051,7 @@
(if (string? c) (make-char (in c idx)) (in c idx)) (if (string? c) (make-char (in c idx)) (in c idx))
(if (nil? default) (if (nil? default)
(error (string "Index " idx " out of bounds, length: " (length c))) (error (string "Index " idx " out of bounds, length: " (length c)))
default)))))))) default)))))))))
(defn core-sort (defn core-sort
"(sort coll) or (sort comparator coll). Comparator may return a boolean or a "(sort coll) or (sort comparator coll). Comparator may return a boolean or a
@ -1908,7 +1909,10 @@
[expr & clauses] [expr & clauses]
(def g (gensym)) (def g (gensym))
(defn make-const [c] (defn make-const [c]
(if (and (struct? c) (= :symbol (c :jolt/type))) # case constants are literals, never evaluated: quote symbols and list
# literals (read as arrays) so e.g. `sym` and a wrapped list `(a b c)` match
# by value rather than resolving/calling.
(if (or (and (struct? c) (= :symbol (c :jolt/type))) (array? c))
@[{:jolt/type :symbol :ns nil :name "quote"} c] @[{:jolt/type :symbol :ns nil :name "quote"} c]
c)) c))
(defn make-test [c] (defn make-test [c]
@ -2565,6 +2569,7 @@
# update — works on both structs and tables # update — works on both structs and tables
(defn core-update [m k f & args] (defn core-update [m k f & args]
(def f (as-fn f))
(core-assoc m k (apply f (core-get m k) args))) (core-assoc m k (apply f (core-get m k) args)))
(defn- ks-rest [ks] (defn- ks-rest [ks]
@ -2914,7 +2919,11 @@
hit)) hit))
(defn core-sequential? [x] (or (tuple? x) (array? x) (pvec? x) (plist? x) (lazy-seq? x))) (defn core-sequential? [x] (or (tuple? x) (array? x) (pvec? x) (plist? x) (lazy-seq? x)))
(defn core-associative? [x] (or (phm? x) (struct? x) (tuple? x) (array? x) (pvec? x) (and (table? x) (not (set? x))))) # Associative = maps and (real) vectors only. pvec is a literal/built vector;
# tuples and lists are seq results, not associative.
(defn core-associative? [x]
(or (pvec? x) (phm? x) (core-sorted-map? x)
(and (struct? x) (nil? (get x :jolt/type)))))
(defn core-ifn? [x] (defn core-ifn? [x]
(or (function? x) (cfunction? x) (keyword? x) (phm? x) (set? x) (tuple? x) (array? x) (pvec? x) (or (function? x) (cfunction? x) (keyword? x) (phm? x) (set? x) (tuple? x) (array? x) (pvec? x)
(and (struct? x) (= :symbol (x :jolt/type))))) (and (struct? x) (= :symbol (x :jolt/type)))))
@ -3109,7 +3118,8 @@
(defn core-counted? [x] (defn core-counted? [x]
(or (pvec? x) (plist? x) (phm? x) (set? x) (tuple? x) (array? x) (string? x))) (or (pvec? x) (plist? x) (phm? x) (set? x) (tuple? x) (array? x) (string? x)))
(defn core-reversible? [x] (or (pvec? x) (tuple? x) (array? x))) # Reversible (supports rseq) = vectors and sorted collections.
(defn core-reversible? [x] (or (pvec? x) (core-sorted-map? x) (core-sorted-set? x)))
(defn core-seqable? [x] (defn core-seqable? [x]
(or (nil? x) (tuple? x) (array? x) (pvec? x) (plist? x) (phm? x) (set? x) (or (nil? x) (tuple? x) (array? x) (pvec? x) (plist? x) (phm? x) (set? x)
(struct? x) (lazy-seq? x) (string? x) (struct? x) (lazy-seq? x) (string? x)

View file

@ -18,9 +18,9 @@
# Baseline: assertions Jolt currently passes across the suite. Raise as Jolt # Baseline: assertions Jolt currently passes across the suite. Raise as Jolt
# improves so a regression (previously-passing assertion breaking) is caught. # improves so a regression (previously-passing assertion breaking) is caught.
(def baseline-pass 3600) (def baseline-pass 3660)
# A file is "clean" when it ran with zero failures AND zero errors. # A file is "clean" when it ran with zero failures AND zero errors.
(def baseline-clean-files 43) (def baseline-clean-files 45)
# Per-file wall-clock budget (seconds). Normal files finish in well under 1s; # Per-file wall-clock budget (seconds). Normal files finish in well under 1s;
# this only fires on infinite-sequence hangs. # this only fires on infinite-sequence hangs.
(def per-file-timeout 6) (def per-file-timeout 6)

View file

@ -15,7 +15,12 @@
["condp" "\"two\"" "(condp = 2 1 \"one\" 2 \"two\" \"other\")"] ["condp" "\"two\"" "(condp = 2 1 \"one\" 2 \"two\" \"other\")"]
["case" ":b" "(case 2 1 :a 2 :b :default)"] ["case" ":b" "(case 2 1 :a 2 :b :default)"]
["case default" ":d" "(case 9 1 :a 2 :b :d)"] ["case default" ":d" "(case 9 1 :a 2 :b :d)"]
["case multi" ":ab" "(case 2 (1 2) :ab 3 :c)"]) ["case multi" ":ab" "(case 2 (1 2) :ab 3 :c)"]
["case symbol const" ":s" "(case 'foo foo :s :default)"]
["case vector const" ":v" "(case [1 2] [1 2] :v :default)"]
["case map const" ":m" "(case {:a 1} {:a 1} :m :default)"]
["case list const" ":l" "(case '(a b) (quote (a b)) :l :default)"]
["case keyword" ":k" "(case :x :x :k :default)"])
(defspec "control / logic" (defspec "control / logic"
["and all true" "3" "(and 1 2 3)"] ["and all true" "3" "(and 1 2 3)"]

View file

@ -34,6 +34,12 @@
["sequential? vector" "true" "(sequential? [1])"] ["sequential? vector" "true" "(sequential? [1])"]
["associative? map" "true" "(associative? {:a 1})"] ["associative? map" "true" "(associative? {:a 1})"]
["associative? vec" "true" "(associative? [1])"] ["associative? vec" "true" "(associative? [1])"]
["associative? list" "false" "(associative? '(1 2))"]
["associative? set" "false" "(associative? #{1})"]
["reversible? vec" "true" "(reversible? [1 2])"]
["reversible? list" "false" "(reversible? '(1 2))"]
["reversible? smap" "true" "(reversible? (sorted-map :a 1))"]
["reversible? hmap" "false" "(reversible? (hash-map :a 1))"]
["indexed? vector" "true" "(indexed? [1])"] ["indexed? vector" "true" "(indexed? [1])"]
["counted? vector" "true" "(counted? [1])"]) ["counted? vector" "true" "(counted? [1])"])