Live Clojure Cookbooks: Ready-to-Use Recipes for Everyday Functional Programming
There's a certain type of Clojure tutorial that starts with "first, let's discuss the philosophical implications of immutability..." — and by paragraph three, you've closed the tab and opened Slack. Look, we love theory here at Loop Code Recur, but sometimes you just need a recipe. Something you can copy, paste, tweak, and ship before your stand-up.
So here's our take on a live cookbook: working recipes for the problems Clojure devs actually hit, with the algorithmic thinking explained just enough to make them stick.
Recipe 1: Deep-Merge Nested Config Maps
The problem: you've got a base config and an environment override, and merge only handles the top level.
(defn deep-merge [a b]
(if (and (map? a) (map? b))
(merge-with deep-merge a b)
b)))
That's it — recursion doing the heavy lifting. Notice the structure: base case (not both maps? take b), recursive case (merge maps, recursing on conflicts). Once you see this shape, you'll spot it everywhere. It's the same skeleton as traversing a tree, because... it is traversing a tree.
Recipe 2: Flatten a Deeply Nested Structure (Without flatten)
Core's flatten nukes everything into a flat list. Often you only want to flatten one level, or flatten while keeping certain things intact:
(defn flatten-once [coll]
(mapcat #(if (sequential? %) % [%]) coll))
(flatten-once [1 [2 3] 4 [5]])
;; => (1 2 3 4 5)
mapcat is the unsung hero of Clojure — it's map followed by concat, which is honestly how half of data pipelines work anyway.
Recipe 3: Group Records by a Computed Key
Every API response ever: a list of records you need bucketed. Maybe you're grouping customers by state code for a US-wide rollout, or orders by week:
(->> orders
(group-by (comp :state :shipping-address))
(map (fn [[state os]] [state (count os)]))
(into (sorted-map)))
The (comp :state :shipping-address) bit is the move to internalize — composing key extractors keeps your transformations declarative. sorted-map gives you alphabetical output for free, which matters when someone's eyeballing a report.
Recipe 4: Write Your Own reduce (Yes, Really)
You use reduce daily. But writing it yourself is a five-minute exercise that permanently upgrades your intuition:
(defn my-reduce [f init coll]
(loop [acc init
remaining coll]
(if (seq remaining)
(recur (f acc (first remaining))
(next remaining))
acc)))
loop/recur is Clojure's iteration workhorse — recur rebinds the loop locals without consuming stack, so this handles millions of elements without a stack overflow. Once you've written this, reduce, transduce, and iterate all stop feeling like magic.
Recipe 5: Sliding Window Over a Sequence
Computing a moving average, diffing consecutive items, rate-limit checks — all sliding window problems:
(defn sliding-pairs [coll]
(map vector coll (rest coll)))
(sliding-pairs [10 12 9 15])
;; => ([10 12] [12 9] [9 15])
Two lines. The lazy rest means this never materializes the whole sequence in memory — great for log processing where "the log" is 4GB of Tuesday.
How to Use This Cookbook
Don't memorize these. Instead, notice the patterns:
- Recursion with a base case → tree-shaped problems (deep-merge, directory walks, JSON traversal)
- map/filter/reduce pipelines → any list-of-records problem
- loop/recur → when you need explicit control over iteration
- Lazy sequences → when the data is big and you only need part of it
That's four patterns covering maybe 80% of day-to-day Clojure data wrangling. Keep this page bookmarked, paste the recipe, adjust the shape of your data, and move on with your life.
And when you're ready to go deeper — say, turning recipe #4 into a full understanding of transducers, or recipe #5 into a rate limiter running in production — you know where to find us. One iteration at a time.