Learn when `fn`, `#()`, `partial`, or a named helper is the clearest choice for higher-order Clojure code.
Anonymous function: A function value created without a permanent top-level name, usually because the logic is short-lived or only useful at one call site.
Anonymous functions are common in Clojure because higher-order functions are common. The real idiomatic question is not “can I write this with fn or #()?” It is “should this logic stay inline at all?”
Anonymous functions are strongest when they keep a transformation local and obvious:
1(map (fn [x] (* x x)) [1 2 3 4])
or, more tersely:
1(map #(* % %) [1 2 3 4])
Both are fine. The choice depends on readability, not on ideological purity.
fn Is the Explicit Formfn is clearer when:
1(filter
2 (fn [{:keys [status retries]}]
3 (and (= status :failed)
4 (< retries 3)))
5 jobs)
This reads better than forcing the same logic into dense % placeholders. Named parameters reduce ambiguity.
#() Is Great for Tiny TransformationsReader shorthand is best when the logic is genuinely short:
1(map #(update % :age inc) users)
or:
1(remove #(zero? (mod % 3)) xs)
The more placeholders you need, the more quickly the shorthand stops being helpful.
1;; Usually too dense:
2(sort-by #(+ (* 10 (:priority %)) (:created-at %)) tasks)
At that point, fn or a named helper usually wins.
%, %1, %2, and %&The shorthand reader form uses placeholder symbols:
% for the first argument%1, %2, and so on for multiple arguments%& for the rest arguments1(map #(+ %1 %2) [1 2 3] [10 20 30])
2;; => (11 22 33)
This is convenient, but it can also become hard to scan. Once the reader has to count placeholders mentally, the terse form is no longer helping.
partial When You Are Only Fixing ArgumentsNot every inline function should be anonymous. If the job is simply to fix some arguments of an existing function, partial often says that more clearly:
1(map (partial + 10) [1 2 3])
2;; => (11 12 13)
Compare that with:
1(map #(+ 10 %) [1 2 3])
Both work. partial is often nicer when the intention is “preconfigure this function,” not “write a new one-off transformation.”
If the anonymous function expresses a real domain rule, naming it usually improves the code.
1(defn retryable-failure? [{:keys [status retries]}]
2 (and (= status :failed)
3 (< retries 3)))
4
5(filter retryable-failure? jobs)
That is better than hiding the business rule inside one filter call. Anonymous functions are for local mechanics, not for every meaningful rule in the program.
Anonymous functions often close over nearby locals:
1(let [cutoff 100]
2 (filter #(> % cutoff) xs))
That is useful, but it also means the function depends on surrounding context. Keep an eye on how much hidden state the closure is carrying. If the inline function needs many outer values, a named helper with explicit arguments may be clearer.
#() when the body is too dense to parse quickly%1 and %2 so heavily that the reader has to decode the expressionpartial or a named helper may describe the intent more clearlyfn is better when argument names and body clarity matter.#() is best for genuinely short inline logic.partial is often clearer when you are only fixing arguments.