Skip to content
apply, partial, comp, juxt
step 1/7

Reading — step 1 of 7

Learn

~3 min readFunctional Combinators, Sets, Errors

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

  • apply without a collection at the end — last arg must be a collection. (apply + 1 2 3) is wrong; (apply + [1 2 3]) is right.
  • comp ordering confusion — right-to-left composition. (comp f g) applies g first.
  • juxt for many functions — output is a vector parallel to input order. Plan accordingly.
  • Reaching for partial when a lambda is clearer#(* % 2) vs (partial * 2). Both work; pick what reads better.
  • Using comp where -> 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…