Use the Clojure REPL to evaluate complete forms, inspect return values, require helper namespaces, work with recent results, and avoid confusing scratch state with saved source.
The basic REPL skill is not typing commands. It is evaluating the right form and reading the returned value accurately.
Clojure code is built from forms. The REPL reads a complete form, evaluates it, prints the value, and waits for the next form.
1(+ 10 20 30)
2;; => 60
3
4(filter even? [1 2 3 4 5 6])
5;; => (2 4 6)
6
7(assoc {:id 1001 :status :new} :status :paid)
8;; => {:id 1001, :status :paid}
This changes the learning loop for Java engineers:
| Java-style question | REPL-style question |
|---|---|
| Did the whole app run? | What value did this form return? |
| What log line should I add? | What expression exposes the value I need? |
| Which class owns this method? | Which namespace holds this function? |
| Do I need a full debugger session? | Can I evaluate the function directly? |
Because Clojure data is literal and printable, use data directly:
1(def sample-order
2 {:order/id 1001
3 :order/lines [{:sku "A1" :qty 2 :price 10}
4 {:sku "B2" :qty 1 :price 25}]})
5
6(map :qty (:order/lines sample-order))
7;; => (2 1)
This is often faster than creating Java fixture objects just to ask a simple question.
1(defn line-total [line]
2 (* (:qty line) (:price line)))
3
4(defn order-total [order]
5 (reduce + (map line-total (:order/lines order))))
6
7(order-total sample-order)
8;; => 45
Once the function is useful, move it to a source file and evaluate it from there. Do not let important behavior live only in your terminal history.
Load standard libraries with require:
1(require '[clojure.string :as str])
2
3(str/split "sku-qty-price" #"-")
4;; => ["sku" "qty" "price"]
Use aliases consistently. They make REPL forms closer to real source code and prevent copy/paste drift.
In many standard REPL sessions, helpers such as doc, source, apropos, and pst are available in user. If not, require them:
1(require '[clojure.repl :refer [doc source apropos pst]])
2
3(doc reduce)
4(apropos "partition")
For Java developers, this is like having API documentation and source lookup inside the running process.
1(range 10)
2;; => (0 1 2 3 4 5 6 7 8 9)
3
4(take 3 *1)
5;; => (0 1 2)
| Var | Meaning |
|---|---|
*1 |
Most recent result |
*2 |
Previous result |
*3 |
Result before that |
*e |
Most recent exception |
Use these for short investigations. Avoid building long, invisible chains that nobody else can reproduce.
Move code from REPL to source when:
| Mistake | Why it hurts |
|---|---|
| Evaluating fragments instead of complete forms | The REPL waits or errors in confusing ways |
Leaving important defs only in the session |
Restart loses the work |
| Copying REPL-only aliases into source without checking namespace forms | Source fails to load |
| Printing everything | You miss Clojure’s value-oriented inspection style |
| Forgetting the current namespace | Vars end up in the wrong place |
*1, *2, *3, and *e for short investigations, not hidden workflows.