Learn what it means for a Clojure function to return another function, how closures work, and when this pattern is simpler than Java-style factories or strategy objects.
If passing a function as an argument lets you choose behavior at the call site, returning a function lets you configure behavior once and reuse it later.
That is the core idea behind this section.
In Java terms, this often replaces small factory classes, strategy objects, or configuration-heavy builders. In Clojure, it is often just a function that returns a function.
Closure: A function value that retains access to bindings from the scope where it was created.
Here is the smallest useful example:
1(defn multiplier [factor]
2 (fn [x]
3 (* factor x)))
4
5(def double-it (multiplier 2))
6(def triple-it (multiplier 3))
7
8(double-it 10)
9;; => 20
The returned function “remembers” factor because it closes over that binding.
That is the important concept:
The closest Java comparison is returning a lambda that captures local variables.
That comparison is useful, but Clojure makes the pattern much more central to ordinary design.
| Java tendency | Clojure tendency |
|---|---|
| Create a small class or strategy object to hold configurable behavior | Return a function that closes over the configuration |
| Treat captured behavior as a special pattern | Treat it as ordinary function construction |
| Reach for interfaces early | Reach for plain functions first when polymorphism is not the real problem |
This does not mean records, protocols, and Java classes disappear. It means you should not reach for a heavier abstraction when a closure already models the problem cleanly.
This is best when the returned behavior has real logic:
1(defn order-validator [minimum-total]
2 (fn [order]
3 (and (= :draft (:order/status order))
4 (>= (:order/total order) minimum-total))))
Now you can build specialized validators:
1(def premium-order? (order-validator 1000M))
2(def standard-order? (order-validator 100M))
partial When The Shape Is SimpleSometimes you are only fixing one or more arguments of an existing function.
1(def premium-discount
2 (partial * 0.90M))
3
4(premium-discount 200M)
5;; => 180.00M
partial is ideal when the returned function is just “this function, with some arguments already supplied.”
But it is not always the clearest choice. If the callback needs explanation or domain meaning, a named fn or defn is often easier to read.
Use a returned function when:
That shape appears constantly in real programs:
| Choose now | Apply later |
|---|---|
| threshold | validation function |
| discount policy | pricing function |
| environment config | adapter function |
| field selection | projection function |
If the code has that shape, returning a function is usually a good fit.
Closures often feel mysterious until you remember what is really happening:
the returned function simply retains access to bindings from the outer call.
1(defn make-greeter [prefix]
2 (fn [name]
3 (str prefix ", " name)))
4
5(def greet-customer (make-greeter "Welcome"))
6(greet-customer "Ava")
7;; => "Welcome, Ava"
No global variable is involved. No object field had to be introduced. The function keeps access to prefix because it was created in that scope.
partialOne subtle point matters during interactive development:
partial captures the function value it receives, not future redefinitions of the var at the REPL.
That means code like this can surprise you while experimenting:
1(defn score [x] (* 2 x))
2(def score-twice (partial score))
If you later redefine score at the REPL, score-twice still holds the old function value. The official Clojure FAQ calls this out directly.
In normal running applications, this is usually irrelevant. In active REPL work, it means:
partial is still correctDo not force this pattern everywhere.
Return a function when the result is genuinely reusable behavior.
Return data when the result is just configuration.
| Better to return a function | Better to return data |
|---|---|
| A validator specialized by a threshold | A map of threshold values |
| A formatter specialized by rendering options | A map of rendering options |
| A scoring function specialized by weighting rules | A weights configuration map |
If callers still need to interpret the result before doing anything useful, returning data may be the clearer design.