ClojureDocs

Nav

Namespaces

halt-when

clojure.core

Available since 1.9 (source)
  • (halt-when pred)
  • (halt-when pred retf)
Returns a transducer that ends transduction when pred returns true
for an input. When retf is supplied it must be a fn of 2 arguments -
it will be passed the (completed) result so far and the input that
triggered the predicate, and its return value (if it does not throw
an exception) will be the return value of the transducer. If retf
is not supplied, the input that triggered the predicate will be
returned. If the predicate never returns true the transduction is
unaffected.
5 Examples
(def letters (set "abcdefghijklmnopqrstuvwxyz"))
(def vowels (set "aeiou"))

;; Remove the vowels from a string.
(transduce (remove vowels) str "hello")
;;=> "hll"

;; Same, but halt when seeing a non-letter (and return that non-letter).
(transduce (comp (remove vowels) (halt-when (complement letters)))
           str "hello")
;;=> "hll"
(transduce (comp (remove vowels) (halt-when (complement letters)))
           str "hello world")
;;=> \space
;; "halt-when" can only really be used with "transduce" (and "into" 
;; as of Clojure 1.11.1), but not with other functions that
;; support transducers like "sequence".

(def v (vec (concat (range 5) [:x])))
(def xf (comp (take 10) (halt-when keyword?) (map inc)))

(transduce xf conj [] v)
;;=> :x

(into [] xf v)
;;=> ClassCastException!
;; Clojure 1.11.0 Alpha 4 updates "into" so that this works and produces:
;;=> :x

(sequence xf v)
;;=> (1 2 3 4 5), wrong result!
;; Using the second argument in halt-when

(transduce
 (comp (halt-when #{30}                ;; stop at 30th item
                  (fn [acc itm] acc))) ;; return all items so far
 (completing conj #(reduce + %))       ;; sums items on exit
 (range))
;; 435
;; Like `take-while`'s complement when we return the completed result so far
(transduce (take-while #(< % 10)) + (range));; => 45
(transduce (halt-when #(>= % 10) (fn [acc _] acc)) + (range));; => 45

;; So it's similar to medley's `take-upto` when `retf` = transduce's `f`
(transduce (medley.core/take-upto #(>= % 10)) conj (range))
;; => [0 1 2 3 4 5 6 7 8 9 10]
(transduce (halt-when #(>= % 10) conj) conj (range))
;; => [0 1 2 3 4 5 6 7 8 9 10]
;; Since Clojure 1.11 we can also use this with e.g. into:
(into {}
        (comp
          (map (juxt :l :n))
          (halt-when (comp (complement number?) second) 
                     (constantly ::invalid)))
        [{:l "A" :n 1}
         {:l "B" :n "not a number!"}
         {:l "C" :n 3}])
;; => ::invalid
;; (remove the B entry to get `=> {"A" 1, "C" 3}`)
See Also

reduce with a transformation of f (xf). If init is not supplied, (f) will be called to produce it....

Added by crimeminister
2 Notes