Anonymous Functions with `fn` and `#()`

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?”

Use Anonymous Functions for Local, Short-Lived Logic

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 Form

fn is clearer when:

  • the body spans multiple lines
  • argument names matter
  • you need more than one parameter
  • you want multiple arities
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 Transformations

Reader 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 arguments
1(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.

Prefer partial When You Are Only Fixing Arguments

Not 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.”

Prefer a Named Function When the Logic Has Domain Meaning

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 and Closures

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.

Common Mistakes

  • using #() when the body is too dense to parse quickly
  • hiding important domain rules inside anonymous functions instead of naming them
  • using placeholders like %1 and %2 so heavily that the reader has to decode the expression
  • forgetting that partial or a named helper may describe the intent more clearly
  • assuming shorter syntax is automatically more idiomatic

Key Takeaways

  • fn 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.
  • Named functions are better for reusable or domain-meaningful behavior.
  • The most idiomatic choice is the one that makes the call site easiest to read.

Ready to Test Your Knowledge?

Loading quiz…
Revised on Thursday, April 23, 2026