Core combinators: apply, fnil, juxt & friends
IntermediateClojure's clojure.core ships a small kit of higher-order helpers that
build new functions from existing ones — no fnclojure.core/fnAnonymous function: (fn [args] body).view on clojuredocs → needed. Once you can spot when
each one fits, point-free code reads naturally and stays short. This page is a
tour of the ones every Clojure programmer eventually leans on: applyclojure.core/applyApply f to the given args (last argument is a seq).view on clojuredocs →,
identityclojure.core/identityReturns its argument unchanged.view on clojuredocs →, constantlyclojure.core/constantlyReturn a fn that always yields the given value.view on clojuredocs →, complementclojure.core/complementReturn a fn whose truthiness is the opposite of f.view on clojuredocs →, every-predclojure.core/every-predCombine predicates with AND; returns a new predicate.view on clojuredocs →, some-fnclojure.core/some-fnCombine predicates with OR; returns a new predicate.view on clojuredocs →, fnilclojure.core/fnilWrap f so nil arguments are replaced by the given defaults.view on clojuredocs →, and
trampolineclojure.core/trampolineBounce through fns returning fns to implement mutual recursion without growing the stack.view on clojuredocs →.
applyclojure.core/applyApply f to the given args (last argument is a seq).view on clojuredocs → — splat a seq into a call
applyclojure.core/applyApply f to the given args (last argument is a seq).view on clojuredocs → is the bridge between a function and a sequence of arguments you
already have. Given (apply f xs), it calls f with the items of xs
spread as individual arguments. The classic use: a variadic function plus a
collection.
You can pass leading positional args before the seq — only the last argument is splatted:
A common practical shape — pipe through a reducer when you have a vector of maps to merge:
identityclojure.core/identityReturns its argument unchanged.view on clojuredocs → and constantlyclojure.core/constantlyReturn a fn that always yields the given value.view on clojuredocs → — the trivial functions
identityclojure.core/identityReturns its argument unchanged.view on clojuredocs → returns its argument unchanged. constantlyclojure.core/constantlyReturn a fn that always yields the given value.view on clojuredocs → builds a function that ignores its arguments and always returns the same value. They feel pointless in isolation; they earn their keep as placeholders when a higher-order function demands a callable.
Real use: split a sequence into "keep" and "drop" groups by a predicate, but keep the original elements unchanged via identityclojure.core/identityReturns its argument unchanged.view on clojuredocs →:
identityclojure.core/identityReturns its argument unchanged.view on clojuredocs → would let you re-key the same group-byclojure.core/group-byBuild a map of (f x) → vector of items grouped by f.view on clojuredocs → if you wanted the elements
in the groups untouched: (group-by identity [\:a :a :b :a :b]). constantlyclojure.core/constantlyReturn a fn that always yields the given value.view on clojuredocs →
shines as a fallback in updateclojure.core/updateReturn a new map with (f (m k)) at k.view on clojuredocs → / assoc-inclojure.core/assoc-inassoc nested inside a path of keys.view on clojuredocs → style updates — "force this to a
fixed value, ignoring whatever was there."
complementclojure.core/complementReturn a fn whose truthiness is the opposite of f.view on clojuredocs → — flip a predicate
(complement pred) returns a new predicate whose truthiness is the opposite of
pred. Use it when you'd otherwise wrap a notclojure.core/notLogical negation; (not nil) and (not false) are true.view on clojuredocs → in a lambda:
(filter non-empty? …) reads as a sentence; (filter #(not (empty? %)) …)
reads as a workaround.
every-predclojure.core/every-predCombine predicates with AND; returns a new predicate.view on clojuredocs → and some-fnclojure.core/some-fnCombine predicates with OR; returns a new predicate.view on clojuredocs → — combine predicates
These two are the AND and OR of predicates. They take any number of predicates
and return a new one that, given an argument, returns true when all /
any of the originals do.
fnilclojure.core/fnilWrap f so nil arguments are replaced by the given defaults.view on clojuredocs → — supply defaults for nil arguments
fnilclojure.core/fnilWrap f so nil arguments are replaced by the given defaults.view on clojuredocs → wraps a function so any nil argument is replaced by a default before the call. It's the cleanest way to handle "first-time" updates in updateclojure.core/updateReturn a new map with (f (m k)) at k.view on clojuredocs →:
The real pattern is "increment a counter that might not exist yet":
Without fnilclojure.core/fnilWrap f so nil arguments are replaced by the given defaults.view on clojuredocs →, the first update :home would call (inc nil) and blow up.
trampolineclojure.core/trampolineBounce through fns returning fns to implement mutual recursion without growing the stack.view on clojuredocs → — mutual recursion without blowing the stack
Clojure's recurclojure.core/recurTail-recursive call to the enclosing loop or fn.view on clojuredocs → only retargets the enclosing loop or function. For mutually recursive calls you would consume stack frames — trampolineclojure.core/trampolineBounce through fns returning fns to implement mutual recursion without growing the stack.view on clojuredocs → fixes that by accepting a function that returns either a value or another zero-arity function, and bouncing through them until a value comes back.
If you replaced (fn [] (odd-step …)) with a direct call, recurring 100k
times would overflow. The bouncing trampoline keeps the JS/JVM stack flat.
Check yourself
? quiz
Which expression most clearly says 'every number in this collection that is positive AND odd'?
Exercise
Build a function counter-bump that takes a map and a key, and increments
the count under that key — defaulting to 0 when the key is missing. Use
fnilclojure.core/fnilWrap f so nil arguments are replaced by the given defaults.view on clojuredocs → and updateclojure.core/updateReturn a new map with (f (m k)) at k.view on clojuredocs → rather than ifclojure.core/ifConditional: (if test then else?).view on clojuredocs →/get.