sort-by, rand-int, shuffle, random-uuid, char-escape-string, and char-name-string move to 20-coll over the two host seams that stay (rand and sort — they ARE the randomness/ordering primitives). Canonical upgrades ride along: sort-by defaults its comparator to compare, so nil sorts FIRST (the kernel fn used host ordering and put nil last); rand-int truncates toward zero via int (the kernel fn floored, wrong for negative n); shuffle is a pure-functional Fisher-Yates over vector assoc and rejects non-collections (a string is seqable but not shuffleable, as on the JVM — the honest gate caught that one); random-uuid builds over rand-int and validates through parse-uuid; the char tables are char-keyed Clojure maps (Clojure's shape — the seed keeps its private code-keyed copies for pr-render). 22 new spec rows. Gate: jpm test exit 0 verified, suite 4698 >= 4660, bench parity with main back-to-back (4733 vs 4817).
164 lines
10 KiB
Text
164 lines
10 KiB
Text
# Specification: functions & higher-order combinators.
|
|
(use ../support/harness)
|
|
|
|
(defspec "functions / definition"
|
|
["fn literal" "3" "((fn [a b] (+ a b)) 1 2)"]
|
|
["fn shorthand" "3" "(#(+ %1 %2) 1 2)"]
|
|
["fn shorthand %" "2" "(#(inc %) 1)"]
|
|
["defn" "5" "(do (defn f [x] (+ x 2)) (f 3))"]
|
|
["multi-arity" "[1 5]" "(do (defn f ([x] x) ([x y] (+ x y))) [(f 1) (f 2 3)])"]
|
|
["variadic" "[1 2 3]" "(do (defn f [& xs] xs) (f 1 2 3))"]
|
|
["variadic with fixed" "[1 [2 3]]" "(do (defn f [a & xs] [a xs]) (f 1 2 3))"]
|
|
["closure captures" "8" "(do (defn adder [n] (fn [x] (+ x n))) ((adder 5) 3))"]
|
|
["recursion" "120" "(do (defn fact [n] (if (< n 2) 1 (* n (fact (dec n))))) (fact 5))"]
|
|
["named fn self-ref" "120" "((fn fact [n] (if (< n 2) 1 (* n (fact (dec n))))) 5)"])
|
|
|
|
(defspec "functions / application"
|
|
["apply" "6" "(apply + [1 2 3])"]
|
|
["apply with leading" "10" "(apply + 1 2 [3 4])"]
|
|
["apply keyword" "1" "(apply :a [{:a 1}])"]
|
|
["partial" "7" "((partial + 5) 2)"]
|
|
["partial multi" "10" "((partial + 1 2) 3 4)"]
|
|
["comp" "4" "((comp inc inc) 2)"]
|
|
["comp order" "5" "((comp inc (fn [x] (* x 2))) 2)"]
|
|
["comp identity" "3" "((comp) 3)"]
|
|
["complement" "true" "((complement even?) 3)"]
|
|
["constantly" "5" "((constantly 5) 1 2 3)"]
|
|
["identity" "7" "(identity 7)"])
|
|
|
|
(defspec "functions / combinators"
|
|
["juxt" "[1 3]" "((juxt first last) [1 2 3])"]
|
|
["fnil" "1" "((fnil inc 0) nil)"]
|
|
["fnil passes value" "6" "((fnil inc 0) 5)"]
|
|
["every-pred true" "true" "((every-pred pos? even?) 4)"]
|
|
["every-pred false" "false" "((every-pred pos? even?) 3)"]
|
|
["some-fn" "true" "((some-fn even? neg?) 3 4)"]
|
|
["memoize" "2" "(do (def c (atom 0)) (def f (memoize (fn [x] (swap! c inc) x))) (f 1) (f 1) (f 2) @c)"]
|
|
["trampoline" "10" "(trampoline (fn f [n acc] (if (zero? n) acc (fn [] (f (dec n) (+ acc 2))))) 5 0)"])
|
|
|
|
# Phase 2 leaf batch (jolt-ded): moved from the Janet seed to 20-coll.clj.
|
|
(defspec "clojure.core / leaf batch (complement fnil munge etc.)"
|
|
["complement true" "true" "((complement pos?) -1)"]
|
|
["complement false" "false" "((complement pos?) 1)"]
|
|
["complement multi" "true" "((complement <) 3 2)"]
|
|
["fnil patches nil" "1" "((fnil inc 0) nil)"]
|
|
["fnil passes non-nil" "6" "((fnil inc 0) 5)"]
|
|
["fnil two defaults" "8" "((fnil + 1 2) nil nil 5)"]
|
|
["fnil only first 3" "[:a :b :c nil]" "((fnil vector :a :b :c) nil nil nil nil)"]
|
|
["fnil in update" "{:k 1}" "(update {} :k (fnil inc 0))"]
|
|
["clojure-version" "true" "(string? (clojure-version))"]
|
|
["bigdec" "3" "(bigdec 3)"]
|
|
["numerator throws" :throws "(numerator 1)"]
|
|
["denominator throws" :throws "(denominator 1)"]
|
|
["supers empty set" "#{}" "(supers 1)"]
|
|
["munge dashes" "\"a_b\"" "(munge \"a-b\")"]
|
|
["munge symbol" "\"x_y\"" "(munge (quote x-y))"]
|
|
["test no-test" ":no-test" "(test (quote foo))"])
|
|
|
|
# Phase 2 leaf batch 2 (jolt-ded): canonical ports of key/val/select-keys/
|
|
# zipmap/merge/merge-with/get-in/memoize/partial/trampoline/some?/true?/false?/
|
|
# max/min/reverse, plus find (previously missing entirely).
|
|
(defspec "clojure.core / leaf batch 2"
|
|
["key" "1" "(key (first {1 :a}))"]
|
|
["val" ":a" "(val (first {1 :a}))"]
|
|
["key non-entry throws" :throws "(key 5)"]
|
|
["find hit" "[:a 1]" "(find {:a 1} :a)"]
|
|
["find miss" "nil" "(find {:a 1} :b)"]
|
|
["find nil value" "[:a nil]" "(find {:a nil} :a)"]
|
|
["find on vector" "[0 :x]" "(find [:x :y] 0)"]
|
|
["select-keys" "{:a 1}" "(select-keys {:a 1 :b 2} [:a])"]
|
|
["select-keys nil val" "{:a nil}" "(select-keys {:a nil :b 2} [:a])"]
|
|
["select-keys missing" "{}" "(select-keys {:a 1} [:z])"]
|
|
["zipmap" "{:a 1 :b 2}" "(zipmap [:a :b] [1 2])"]
|
|
["zipmap uneven" "{:a 1}" "(zipmap [:a :b] [1])"]
|
|
["zipmap nil val" "{:a nil}" "(zipmap [:a] [nil])"]
|
|
["merge" "{:a 1 :b 2}" "(merge {:a 1} {:b 2})"]
|
|
["merge later wins" "{:a 2}" "(merge {:a 1} {:a 2})"]
|
|
["merge nil arg" "{:a 1}" "(merge {:a 1} nil)"]
|
|
["merge nil first" "{:a 1}" "(merge nil {:a 1})"]
|
|
["merge all nil" "nil" "(merge nil nil)"]
|
|
["merge empty" "nil" "(merge)"]
|
|
["merge entry pair" "{:a 1 :b 2}" "(merge {:a 1} [:b 2])"]
|
|
["merge-with" "{:a 3}" "(merge-with + {:a 1} {:a 2})"]
|
|
["merge-with disjoint" "{:a 1 :b 2}" "(merge-with + {:a 1} {:b 2})"]
|
|
["merge-with nil-val present" "{:a 1}" "(merge-with (fn [a b] (or a b)) {:a nil} {:a 1})"]
|
|
["get-in" "1" "(get-in {:a {:b 1}} [:a :b])"]
|
|
["get-in missing" ":nf" "(get-in {:a 1} [:z :y] :nf)"]
|
|
["get-in nil value present" "nil" "(get-in {:a {:b nil}} [:a :b] :nf)"]
|
|
["get-in empty path" "{:a 1}" "(get-in {:a 1} [])"]
|
|
["memoize" "2" "(do (def c (atom 0)) (def f (memoize (fn [x] (swap! c inc) x))) (f 1) (f 1) (f 2) (deref c))"]
|
|
["memoize caches nil" "1" "(do (def c (atom 0)) (def f (memoize (fn [x] (swap! c inc) nil))) (f 1) (f 1) (deref c))"]
|
|
["partial" "6" "((partial + 1 2) 3)"]
|
|
["partial no extra" "3" "((partial + 1 2))"]
|
|
["partial many fixed" "15" "((partial + 1 2 3 4) 5)"]
|
|
["trampoline" "10" "(trampoline (fn f [n acc] (if (zero? n) acc (fn [] (f (dec n) (+ acc 2))))) 5 0)"]
|
|
["some? true" "true" "(some? 0)"]
|
|
["some? false" "false" "(some? nil)"]
|
|
["true?/false?" "[true false false]" "[(true? true) (true? 1) (false? nil)]"]
|
|
["max" "3" "(max 1 3 2)"]
|
|
["min" "1" "(min 3 1 2)"]
|
|
["max single" "5" "(max 5)"]
|
|
["max non-number throws" :throws "(max 1 :a)"]
|
|
["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])"])
|
|
|
|
# Phase 2 leaf batch 4 (jolt-ded): sort-by (canonical: compare-defaulted, over
|
|
# the host sort seam), rand-int (canonical truncation via int), pure
|
|
# Fisher-Yates shuffle, random-uuid over parse-uuid, char tables as
|
|
# char-keyed Clojure maps. rand and sort stay: they ARE the host seams.
|
|
(defspec "clojure.core / leaf batch 4"
|
|
["sort-by keyfn" "[[1 :b] [2 :a]]" "(sort-by first [[2 :a] [1 :b]])"]
|
|
["sort-by string keys" "(quote (\"a\" \"bb\" \"ccc\"))" "(sort-by count [\"ccc\" \"a\" \"bb\"])"]
|
|
["sort-by comparator" "[3 2 1]" "(sort-by identity > [1 3 2])"]
|
|
["sort-by 3way cmp" "[3 2 1]" "(sort-by identity (fn [a b] (- b a)) [1 3 2])"]
|
|
["sort-by mixed nil" "[nil 1 2]" "(sort-by identity [2 nil 1])"]
|
|
["sort-by empty" "()" "(sort-by first [])"]
|
|
["sort-by nil coll" "()" "(sort-by first nil)"]
|
|
["rand-int range" "true" "(every? (fn [_] (let [r (rand-int 5)] (and (int? r) (<= 0 r 4)))) (range 50))"]
|
|
["rand-int zero" "0" "(rand-int 1)"]
|
|
["shuffle is permutation" "true" "(= (sort (shuffle [5 3 1 4 2])) [1 2 3 4 5])"]
|
|
["shuffle returns vector" "true" "(vector? (shuffle [1 2 3]))"]
|
|
["shuffle empty" "[]" "(shuffle [])"]
|
|
["shuffle non-coll throws" :throws "(shuffle 5)"]
|
|
["random-uuid is uuid" "true" "(uuid? (random-uuid))"]
|
|
["random-uuid v4 shape" "true" "(boolean (re-matches #\"[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\" (str (random-uuid))))"]
|
|
["random-uuid distinct" "true" "(not= (random-uuid) (random-uuid))"]
|
|
["char-escape newline" "\"\\\\n\"" "(char-escape-string \\newline)"]
|
|
["char-escape quote" "true" "(= 2 (count (char-escape-string \\\")))"]
|
|
["char-escape none" "nil" "(char-escape-string \\a)"]
|
|
["char-name space" "\"space\"" "(char-name-string \\space)"]
|
|
["char-name newline" "\"newline\"" "(char-name-string \\newline)"]
|
|
["char-name none" "nil" "(char-name-string \\a)"])
|