Round 3 of the seed shrink. To the overlay: identity, constantly, neg?,
even?, odd? (20-coll, ahead of their first in-tier uses), not= and unreduced
(00-syntax — the kernel and seq tiers use them), ==, ensure-reduced,
halt-when, parse-boolean, parse-uuid, newline, seque, array-seq, to-array-2d,
and the masking unchecked-byte/short/char/float/double coercions. parse-uuid
validates via re-matches over a new __make-uuid host binding (overlay source
can't write :jolt/type map literals). memfn moves to 30-macros as a working
macro over the .method call sugar instead of a fn that throws.
Behavior fixes toward Clojure, each with spec rows: == now throws on
non-numbers instead of comparing them, and halt-when is the canonical
::halt-map version (the halt value replaces the whole reduction result, no
double completion). list? and map-entry? stay in the seed — both are
representation-coupled (plist/tuple checks).
clojure-test-suite goes 4701 -> 4700: update.cljc expects
(update {:k 1} :k identity 1 2 3 4) to throw an arity error, and jolt fns
don't enforce fixed arity anywhere (pre-existing, language-wide — the seed's
Janet identity threw natively). Filed as jolt-6xn; fixing it should flip
several suite rows at once.
65 lines
3.8 KiB
Text
65 lines
3.8 KiB
Text
# Specification: transducers.
|
|
(use ../support/harness)
|
|
|
|
(defspec "transducers / into"
|
|
["map xform" "[2 3 4]" "(into [] (map inc) [1 2 3])"]
|
|
["filter xform" "[2 4]" "(into [] (filter even?) [1 2 3 4])"]
|
|
["remove xform" "[1 3]" "(into [] (remove even?) [1 2 3 4])"]
|
|
["take xform" "[1 2]" "(into [] (take 2) [1 2 3 4])"]
|
|
["drop xform" "[3 4]" "(into [] (drop 2) [1 2 3 4])"]
|
|
["take-while xform" "[1 2]" "(into [] (take-while (fn [x] (< x 3))) [1 2 3 1])"]
|
|
["keep xform" "[1 3]" "(into [] (keep (fn [x] (if (odd? x) x nil))) [1 2 3 4])"]
|
|
["map-indexed xform" "[[0 :a] [1 :b]]" "(into [] (map-indexed vector) [:a :b])"]
|
|
["mapcat xform" "[1 1 2 2]" "(into [] (mapcat (fn [x] [x x])) [1 2])"]
|
|
["cat xform" "[1 2 3 4]" "(into [] cat [[1 2] [3 4]])"]
|
|
["into a set" "#{2 3 4}" "(into #{} (map inc) [1 2 3])"])
|
|
|
|
# transducer comp applies left-to-right: (comp (map a) (filter b)) maps then filters
|
|
(defspec "transducers / compose"
|
|
["comp map+filter" "[2 4 6 8]" "(into [] (comp (map (fn [x] (* x 2))) (filter even?)) [1 2 3 4])"]
|
|
["comp filter+map" "[2 4]" "(into [] (comp (filter odd?) (map inc)) [1 2 3 4])"]
|
|
["comp three" "[2]" "(into [] (comp (map inc) (filter even?) (take 1)) [1 2 3 4])"])
|
|
|
|
(defspec "transducers / transduce & sequence"
|
|
["transduce sum" "9" "(transduce (map inc) + [1 2 3])"]
|
|
["transduce init" "19" "(transduce (map inc) + 10 [1 2 3])"]
|
|
["transduce filter" "6" "(transduce (filter even?) + [1 2 3 4])"]
|
|
["sequence xform" "[2 3 4]" "(sequence (map inc) [1 2 3])"]
|
|
["eduction" "[2 3 4]" "(into [] (eduction (map inc) [1 2 3]))"]
|
|
["completing" "9" "(transduce (map inc) (completing +) 0 [1 2 3])"])
|
|
|
|
# halt-when replaces the WHOLE reduction result with the halting input (or
|
|
# with (retf acc input)) — Clojure's ::halt map protocol, unwrapped by the
|
|
# transducer's completion arity.
|
|
(defspec "transducers / halt-when"
|
|
["halt returns the halting input" "7"
|
|
"(transduce (halt-when (fn [x] (> x 5))) conj [1 2 7 3])"]
|
|
["no halt is a plain reduction" "[1 2 3]"
|
|
"(transduce (halt-when (fn [x] (> x 5))) conj [1 2 3])"]
|
|
["retf combines acc and input" "[[1 2] 7]"
|
|
"(transduce (halt-when (fn [x] (> x 5)) (fn [r i] [r i])) conj [1 2 7 3])"]
|
|
["halt-when through into" "3"
|
|
"(into [] (halt-when odd?) [2 4 3 6])"])
|
|
|
|
# A `take`/`take-while` transducer returns `reduced`, which must short-circuit
|
|
# the reduction so transducing over an INFINITE seq terminates rather than
|
|
# realizing it eagerly.
|
|
(defspec "transducers / short-circuit over infinite seqs"
|
|
["into take (range)" "[0 1 2 3 4]" "(into [] (take 5) (range))"]
|
|
["transduce take (range)" "3" "(transduce (take 3) + 0 (range))"]
|
|
["sequence take (range)" "[0 1 2 3 4]" "(sequence (take 5) (range))"]
|
|
["take-while over (range)" "[0 1 2]" "(into [] (take-while (fn [x] (< x 3))) (range))"]
|
|
["comp take over (range)" "[1 3 5]" "(into [] (comp (filter odd?) (take 3)) (range))"]
|
|
["into take iterate" "[0 1 2 3 4]" "(into [] (take 5) (iterate inc 0))"])
|
|
|
|
# `reduce` itself honors `reduced`, so a reducing fn that returns `reduced`
|
|
# terminates even over an infinite seq.
|
|
(defspec "reduce / honors reduced"
|
|
["reduced short-circuits inf" "105"
|
|
"(reduce (fn [a x] (if (> a 100) (reduced a) (+ a x))) 0 (range))"]
|
|
["reduce take inf" "10" "(reduce + (take 5 (range)))"]
|
|
["reduce no-init first elem" "6" "(reduce + [1 2 3])"]
|
|
["reduce no-init single" "42" "(reduce + [42])"]
|
|
["reduce empty calls f" "0" "(reduce + [])"]
|
|
["reduce with-init" "16" "(reduce + 10 [1 2 3])"]
|
|
["reduce reduced immediate" ":x" "(reduce (fn [a x] (reduced :x)) :init [1 2 3])"])
|