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)))
-> for “value is the first argument” (map updates, object-ish transforms)->> for “value is the last argument” (sequence pipelines)