Browse Clojure Foundations for Java Developers

Threading Macros

Make transformation pipelines readable with ->, ->>, and the conditional threading variants some-> and cond->.

Threading macros turn nested function calls into linear, readable pipelines. For Java developers, they feel like the functional version of method chaining or Streams—except they work with any function.

-> (Thread-First)

-> inserts the current value as the first argument of the next form.

1(require '[clojure.string :as str])
2
3(def user {:name "Ada" :age 30 :location "NYC"})
4
5(-> user
6    (assoc :age 31)
7    (dissoc :location)
8    (update :name str/upper-case))
9;; => {:name "ADA", :age 31}

This is great for “update this map” workflows because assoc, dissoc, and update take the map first.

->> (Thread-Last)

->> inserts the current value as the last argument of the next form. That matches most sequence functions (map, filter, remove, reduce, …).

1(->> [1 2 3 4 5]
2     (filter even?)
3     (map #(* % 10))
4     (reduce + 0))
5;; => 60

some-> (Nil-Safe Threading)

some-> stops the pipeline if any step returns nil (a common way to avoid “null checks”):

1(some-> {:user {:email "a@b.com"}}
2        :user
3        :email
4        str/lower-case)
5;; => "a@b.com"

If any step produces nil, the whole expression returns nil.

cond-> (Conditional Threading)

cond-> applies steps only when their condition is true:

1(defn normalize-user [u admin?]
2  (cond-> u
3    true   (update :name str/trim)
4    admin? (assoc :role :admin)))

Quick Rule

  • use -> for “value is the first argument” (map updates, object-ish transforms)
  • use ->> for “value is the last argument” (sequence pipelines)

Knowledge Check: Threading

### When is `->>` usually the better choice than `->`? - [x] When you’re building a sequence pipeline with `map`/`filter`/`reduce` (they take the collection last). - [ ] When you want to mutate Java objects in place. - [ ] When you want macros to run at runtime. - [ ] When your functions take no arguments. > **Explanation:** Most sequence functions take the collection as their last argument, so `->>` reads naturally. ### What does `some->` do that `->` does not? - [x] It stops and returns `nil` if any step produces `nil`. - [ ] It runs steps in parallel for performance. - [ ] It guarantees the result is non-nil. - [ ] It forces lazy sequences to realize. > **Explanation:** `some->` is “nil-safe threading.” It’s a compact alternative to nested `when-let` / `if-let` checks. ### What is `cond->` used for? - [x] Conditionally applying transformations in a pipeline based on boolean tests. - [ ] Choosing between `->` and `->>` automatically. - [ ] Defining new functions from existing ones. - [ ] Converting a map to a vector. > **Explanation:** `cond->` keeps “optional steps” readable without deeply nested `if`/`when`.
Revised on Friday, April 24, 2026