Compared to other languages, I've found Clojure makes it much easier to iteratively turn an idea into code.
Say you're writing a pure function...
You start with just data and a sense of how the output might look. Let's say I have APIs providing me with a user record and a list of transactions, and I want to get that person's balance...
Maybe you start with some canned data
(let [person {:name "Matt"
:id 12345
:current_balance 100.10}
txns [{:person_id 12345 :amt 10}
{:person_id 44444 :amt 0.5}
{:person_id 55555 :amt 10}
{:person_id 12345 :amt 11}
{:person_id 12345 :amt -5}
{:person_id 66666 :amt 3}]]
(->> txns
count)
)
=> 6
and maybe you want to filter the transactions to just that user
(let [person {:name "Matt"
:id 12345
:current_balance 100.10}
txns [{:person_id 12345 :amt 10}
{:person_id 44444 :amt 0.5}
{:person_id 55555 :amt 10}
{:person_id 12345 :amt 11}
{:person_id 12345 :amt -5}
{:person_id 66666 :amt 3}]]
(->> txns
(filter #(= (:id person)
(:person_id %)))
count)
)
=> 3
then you want to extract the amount for each of those
(let [person {:name "Matt"
:id 12345
:current_balance 100.10}
txns [{:person_id 12345 :amt 10}
{:person_id 44444 :amt 0.5}
{:person_id 55555 :amt 10}
{:person_id 12345 :amt 11}
{:person_id 12345 :amt -5}
{:person_id 66666 :amt 3}]]
(->> txns
(filter #(= (:id person)
(:person_id %)))
(map :amt))
)
=> (10 11 -5)
and sum those amounts
(let [person {:name "Matt"
:id 12345
:current_balance 100.10}
txns [{:person_id 12345 :amt 10}
{:person_id 44444 :amt 0.5}
{:person_id 55555 :amt 10}
{:person_id 12345 :amt 11}
{:person_id 12345 :amt -5}
{:person_id 66666 :amt 3}]]
(->> txns
(filter #(= (:id person)
(:person_id %)))
(map :amt)
(apply +))
)
=> 16
and add that to the existing balance
(let [person {:name "Matt"
:id 12345
:current_balance 100.10}
txns [{:person_id 12345 :amt 10}
{:person_id 44444 :amt 0.5}
{:person_id 55555 :amt 10}
{:person_id 12345 :amt 11}
{:person_id 12345 :amt -5}
{:person_id 66666 :amt 3}]
bal-change (->> txns
(filter #(= (:id person)
(:person_id %)))
(map :amt)
(apply +))]
(+ bal-change (:current_balance person))
)
=> 116.1
the function is done!
(defn update-person-balance [{:keys [id current_balance]} txns]
(let [bal-change (->> txns
(filter #(= id
(:person_id %)))
(map :amt)
(apply +))]
(+ bal-change current_balance)))
I posted here as a series of snippets, but this would have been a single block of code, refined and evaluated and refined and evaluated, over and over, in a REPL-integrated editor. It feels like sketching a portrait or sculpting clay.
"But I can do that in any ol' REPL!" you might say. Try Clojure -- really try it, with an nREPL connected to your running application and to your editor, capturing inputs, playing them back, writing evaluation output as comments -- and then tell me you want to go back to your old REPL. A notebook environment is similar, though clumsier IMO.
Come for the REPL-driven-development, stay for the immutability, the Java library ecosystem, the lisp syntax, the concurrency support, etc. I know going on and on about how great Clojure development is is basically a meme at this point, but that's because it's really great!