Browse Clojure Foundations for Java Developers

Defining Functions with `defn`

Use `defn` as the normal way to publish named functions, and understand what it adds beyond `def` plus `fn`.

defn is the standard way to define a named function in a namespace.

You can think of it as the idiomatic wrapper around “create a var whose value is a function,” but that summary is only useful if you keep the everyday workflow in view:

  • defn is how APIs are normally introduced
  • defn keeps function definitions readable
  • defn supports docstrings, metadata, and multiple arities in one place

For Java developers, the main shift is that you are not adding a method to a class. You are binding a function value to a name in a namespace.

The Basic Shape

1(defn greet
2  "Return a greeting for the supplied user name."
3  [name]
4  (str "Hello, " name "!"))

That one form gives you:

  • a namespace var named greet
  • a function value stored in that var
  • a docstring attached as metadata

At the REPL:

1(greet "Ada")
2;; => "Hello, Ada!"

The Java comparison is useful only up to a point. greet behaves like a callable API entry point, but it is not attached to an object instance or class hierarchy the way a Java method is.

Why Not Write def And fn Manually?

You can:

1(def greet
2  (fn [name]
3    (str "Hello, " name "!")))

That is valid Clojure. But defn is clearer and scales better as soon as you want:

  • a docstring
  • metadata like ^:private
  • multiple arities
  • a normal, recognizable namespace API form

In other words, defn is not “magic.” It is the form the language community expects you to use for named functions.

Multi-Arity Functions

One of the biggest advantages of defn for Java engineers is that overloaded call shapes stay together in one definition:

1(defn describe-order
2  ([order-id]
3   (str "Order " order-id))
4  ([order-id status]
5   (str "Order " order-id " is " status)))

That is similar in purpose to Java method overloads, but the result is still one named function value with several supported arities.

This often reads better than creating multiple helper names when the behavior is clearly the same operation with different input shapes.

Variadic Functions

defn also supports a rest argument:

1(defn average
2  [& xs]
3  (/ (reduce + xs) (count xs)))

This is conceptually similar to Java varargs, but it stays in the same value-oriented function model as the rest of Clojure.

Metadata And Visibility

You can attach metadata directly in the definition:

1(defn ^:private parse-port [s]
2  (Long/parseLong s))

^:private means the var is private to the namespace. That is closer to namespace-level visibility control than to class-member access modifiers.

This is a common pattern for keeping internal helpers out of the public namespace API while still writing ordinary top-level functions.

A Better Java-To-Clojure Translation

A common Java instinct is to create a class just to hold related methods.

In Clojure, the better translation is usually:

  • put related functions in one namespace
  • keep shared rules in pure functions
  • use data and function composition instead of method dispatch by default

For example:

1(ns orders.pricing)
2
3(defn line-total [{:keys [qty unit-price]}]
4  (* qty unit-price))
5
6(defn order-total [lines]
7  (reduce + 0M (map line-total lines)))

That is often all you need. No utility class. No object wrapper. Just a namespace of related functions.

When defn Is The Wrong Tool

defn is for named functions at the top level of a namespace.

Inside a higher-order function or a one-off call, use fn or the shorthand #() when it stays readable:

1(map (fn [n] (* n 2)) [1 2 3])

If you find yourself trying to use defn inside another function body, stop and ask whether you really want a local helper, an anonymous function, or a top-level reusable definition.

Knowledge Check

### Why is `defn` usually preferred over `def` plus `fn` for named functions? - [x] It is the idiomatic form and makes docstrings, metadata, and arities clearer - [ ] It produces faster bytecode in every case - [ ] It is the only way to create a function - [ ] It automatically makes all functions private > **Explanation:** `defn` is mainly a readability and maintainability tool. It packages the common function-definition shape in the form readers expect. ### What is the most accurate Java comparison for `defn`? - [x] It creates a namespace-level named function, not a method attached to a class instance - [ ] It is exactly the same as a mutable instance method - [ ] It behaves like a Java constructor - [ ] It creates an enum constant > **Explanation:** The important shift is from class-owned methods to namespace-owned function values. ### When are multiple arities in one `defn` especially helpful? - [x] When one operation has a few closely related call shapes - [ ] When every branch should mutate shared state differently - [ ] When you want to avoid using functions entirely - [ ] When the code should only run from Java > **Explanation:** Multiple arities are a good fit when the same conceptual operation accepts different argument counts cleanly. ### What is the better tool for an anonymous one-off function passed to `map`? - [x] `fn` or `#()` - [ ] `defn` - [ ] `def` - [ ] `ns` > **Explanation:** `defn` is for named top-level functions. Anonymous local behavior usually belongs in `fn` or a reader shorthand.
Revised on Friday, April 24, 2026