Reading — step 1 of 7
Learn
Clojure's higher-order toolkit goes beyond map/filter/reduce. apply, partial, comp, and juxt are the function-manipulation primitives. "Joy of Clojure" treats them as fundamental to functional style.
apply — variadic function call
(apply + [1 2 3 4 5]) ;; 15 — applies + to the elements of the list
(apply str ["a" "b" "c"]) ;; "abc"
(apply max [3 1 4 1 5 9]) ;; 9
(apply f coll) calls f with the elements of coll as separate arguments. Useful when you have a list of args but the function expects them spread.
With leading positional args:
(apply str "prefix-" ["a" "b" "c"]) ;; "prefix-abc"
First few arguments are positional; the last argument MUST be a collection.
partial — partial application
Returns a new function with some arguments pre-supplied:
(def add5 (partial + 5))
(add5 10) ;; 15
(def greet (partial str "Hello, "))
(greet "Ada") ;; "Hello, Ada"
(map (partial * 2) [1 2 3]) ;; (2 4 6)
Like bind in C++ or bind/apply.bind in JS. Cleaner than writing a wrapper lambda for the common case.
comp — function composition
(def first-uppercase (comp clojure.string/upper-case first))
(first-uppercase ["hello" "world"]) ;; "HELLO"
(comp f g h) is equivalent to (fn [x] (f (g (h x)))). Reads right-to-left, like Haskell's ..
(map (comp inc inc) [1 2 3]) ;; (3 4 5)
Compose any number of functions:
(def pipeline (comp str inc dec))
;; equivalent to (fn [x] (str (inc (dec x))))
juxt — multiple functions, single input
((juxt :name :age) {:name "Ada" :age 36}) ;; ["Ada" 36]
((juxt count first last) [1 2 3 4 5]) ;; [5 1 5]
juxt runs N functions on the same argument and collects results in a vector. Great for extracting multiple fields or computing summary stats in one pass:
(def stats (juxt count #(reduce + %) #(reduce min %) #(reduce max %)))
(stats [3 1 4 1 5 9 2 6])
;; [8 31 1 9] — count, sum, min, max
complement — boolean negation
(def odd? (complement even?))
(odd? 5) ;; true
(filter (complement empty?) ["a" "" "b" ""])
;; ("a" "b")
Negates a predicate. (complement f) is (fn [& args] (not (apply f args))).
constantly — function returning a constant
(def always-42 (constantly 42))
(always-42) ;; 42
(always-42 :x :y) ;; 42 — ignores arguments
Useful as a callback that should ignore inputs.
Threading via comp vs ->
The -> macro is for value threading; comp is for function composition. They look similar but operate at different levels:
;; ->: thread a value through forms
(-> 5 inc inc str) ;; "7"
;; comp: compose into one function, then call:
((comp str inc inc) 5) ;; "7"
-> is value-first (data pipeline). comp is function-first (function-building). Both have their place.
Common mistakes
applywithout a collection at the end — last arg must be a collection.(apply + 1 2 3)is wrong;(apply + [1 2 3])is right.compordering confusion — right-to-left composition.(comp f g)applies g first.juxtfor many functions — output is a vector parallel to input order. Plan accordingly.- Reaching for
partialwhen a lambda is clearer —#(* % 2)vs(partial * 2). Both work; pick what reads better. - Using
compwhere->would — for value pipelines,->is usually cleaner.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…