Files
2026-09-14 08:53:09 -09:00

9.9 KiB

REPL-Driven Development

What is a REPL?

A REPL is an acronym for Read-Eval-Print Loop. It is an interactive programming environment where code is parsed, executed, and results are printed back — in a continuous cycle.

The four stages of the REPL cycle:

  1. Read — Your input is parsed into a data structure (in Clojure, into a form)
  2. Eval — The form is evaluated/executed
  3. Print — The result is printed back to you
  4. Loop — It goes back to step 1, ready for your next input

Think of it like a conversation with the language. You say something, it responds, you say something else, it responds — but you're programming.

Why REPLs Matter

REPLs enable immediate feedback. Instead of writing a file, compiling, running, seeing an error, fixing, and repeating — you evaluate something and see the result instantly. This dramatically speeds up learning and development.

REPL-Driven Development (RDD)

REPL-Driven Development is a workflow where you build and explore your program by sending code from your editor to a running REPL, rather than through a traditional edit-compile-run cycle.

The Editor as Your REPL Interface

In the real world, you don't type into a terminal REPL. You use your editor as the REPL interface:

  • Calva (VS Code) — sends code from your buffer to a connected nREPL, shows results inline
  • CIDER (Emacs) — sends code from your buffer to a connected nREPL, shows results in a REPL buffer
  • Conjure (NeoVim) — sends code from your buffer to a connected nREPL, shows results inline
  • vim-fireplace (Vim) — another popular vim Clojure integration
  • Cursive (IntelliJ) — full Clojure IDE with REPL, autocomplete, and debugging
  • Deps-new / Shadow-cljs / etc. — other editor integrations via nREPL

Your editor becomes the workspace. You write code, evaluate pieces of it, inspect results, iterate — all without leaving your editing environment.

How It Works

  1. Start an nREPL server (one time) — a background process that listens for connections
  2. Connect your editor to the nREPL — your editor sends code to the REPL, receives results
  3. Edit and evaluate — write code in your editor, send it to the REPL with a keystroke
  4. Explore and iterate — try ideas, redefine functions, inspect data, all in real time
  5. Keep state — your atoms, bindings, and defined functions persist in the REPL session
  6. Save working code — the code that works in the REPL lives in your source files

The REPL as a Workspace

The REPL is your working environment, not just a debugging tool. Key advantages:

  • Live exploration — evaluate a function call before you've even written the function
  • Partial implementation — stub out functions and test the parts that exist
  • Refactoring — redefine a function and immediately see the impact everywhere
  • Debugging — inspect intermediate values, bind names to data, step through logic
  • Composition — build complex logic by combining small tested pieces in the REPL

RDD Workflow in Practice

With your editor connected to an nREPL server, your workflow looks like this:

;; 1. Load your namespace (once, from your editor)
(require '[learnclj :as lc])
(in-ns 'learnclj)

;; 2. Explore existing functions — evaluate from your editor
(greet "Alice")
;; => "Hello, Alice!"

;; 3. Try something new — send a form to the REPL from your editor
(map #(str (upper-case %) "!") ["hello" "world"])
;; => ("HELLO!" "WORLD!")

;; 4. Define a new helper — write it in your buffer, evaluate it
(defn exclaim [s] (str (str/upper-case s) "!"))
(exclaim "hello")
;; => "HELLO!"

;; 5. Refactor — change the definition in your buffer, re-evaluate
(defn exclaim [s] (str (str/upper-case s) "!!"))
(exclaim "hello")
;; => "HELLO!!"

;; 6. Compose with existing code
(map exclaim ["a" "b" "c"])
;; => ("A!!" "B!!" "C!!")

;; 7. The working code is already in learnclj.clj — your source files

Your editor sends each form to the REPL. The REPL returns results to your editor. You iterate until satisfied. The code you wrote and tested is your source code.

Key Principles

  • Keep the nREPL running — don't restart it unnecessarily. State persists.
  • Explore before you commit — try ideas in the REPL to validate them before writing them into source
  • Small steps — each form you send should be small enough to understand instantly
  • Don't over-rely on the REPL — eventually the REPL code should live in source files for reproducibility and version control
  • Use clojure.repl functions — doc, find-doc, apropos, source are invaluable
  • Your editor is the interface — learn the send/eval shortcuts for your editor (Calva, CIDER, etc.)

Starting an nREPL Server

Babashka (lightweight, fast)

Babashka is a fast, lightweight Clojure interpreter with built-in nREPL support.

# Start nREPL server (default port 1667)
bb --nrepl-server

# Specify a port
bb --nrepl-server --nrepl-port 9999

Why Babashka nREPL?

  • Fast startup — near-instant compared to full Clojure/JVM
  • No JVM dependency — uses GraalVM native image, so no Java needed
  • Script-friendly — works with .clj files directly
  • Full nREPL protocol — compatible with all nREPL clients

Clojure (JVM-based)

Important

: The clj command needs a deps.edn with an :nREPL alias that includes the nREPL dependency. Without one, these commands will not work.

Option A — Quick start (requires a deps.edn)

Add this to a deps.edn in your project root:

{:aliases
 {:nREPL
  {:extra-deps
   {nrepl/nrepl {:mvn/version "1.12.0"}}}}}

Then start a headless nREPL server:

clj -M:nREPL -m nrepl.cmdline

Option B — Plain Clojure REPL (no nREPL server)

If you just want an interactive REPL for experimentation (no editor integration):

clj

This starts a standard Clojure REPL (REPL-y). You type code directly into the terminal. No deps.edn, no nREPL needed.

Option C — Global alias (one-time setup)

Add the nREPL alias to your global config ~/.clojure/deps.edn:

{:aliases
 {:nREPL
  {:extra-deps
   {nrepl/nrepl {:mvn/version "1.12.0"}}}}}

Then from any directory:

clj -M:nREPL -m nrepl.cmdline

Connecting from Your Editor

Calva (VS Code)

  1. Start an nREPL server (bb --nrepl-server or clj -M:nREPL -m nrepl.cmdline)
  2. Ctrl+Alt+C, Ctrl+Alt+P — Calva connects automatically

Conjure (NeoVim)

  1. Install Conjure via your plugin manager (vim-plug, packer, lazy, etc.)
  2. Start an nREPL server (bb --nrepl-server or clj -M:nREPL -m nrepl.cmdline)
  3. :ConjureConnect localhost <port> — connect to the REPL
  4. Send code with <localleader>ee (evaluate form under cursor)
  5. Results appear inline below the code
  6. Open the REPL buffer with :ConjureShow

Key Conjure shortcuts:

  • <localleader>ee — evaluate form under cursor
  • <localleader>ef — evaluate file
  • <localleader>eb — evaluate buffer
  • <localleader>er — evaluate root form
  • <localleader>ew — evaluate word under cursor
  • <localleader>ep — evaluate previous evaluation
  • <localleader>em — evaluate marked form
  • <localleader>e! — evaluate and replace form with result
  • <localleader>ece — evaluate form and comment the result
  • <localleader>gd — get definition under cursor
  • K — get documentation under cursor (no prefix, built-in)
  • E (visual mode) — evaluate visual selection

Note: <localleader> defaults to , but is typically remapped in your config (e.g., to \ or Space). Check :map <localleader> to see yours.

  • :ConjureConnect — connect to an nREPL server
  • :ConjureShow — show the REPL buffer
  • :ConjureClear — clear the REPL buffer

vim-fireplace (Vim)

  1. Install via your plugin manager
  2. Start an nREPL server
  3. :PlFigConnect localhost <port> — connect
  4. Evaluate with <leader>a (send current form)
  5. Results appear in a split buffer

Cursive (IntelliJ IDEA)

  1. Install the Cursive plugin (bundled with paid IntelliJ or available as a trial)
  2. Start a REPL: Run → Edit Configurations → + → Local Clojure REPL — choose your build tool (Leiningen/Deps)
    • Or connect to a running REPL: Run → Edit Configurations → + → Remote Clojure REPL — enter host/port
  3. Ctrl+Enter — evaluate form before cursor (works anywhere on the form)
  4. Enter — execute if cursor is at end of a valid form
  5. Ctrl+Shift+E — load file in REPL (loads current file + dependencies)
  6. Ctrl+Alt+E — search REPL history (find and re-execute previous evaluations)
  7. Run → Clojure REPL → Switch REPL Namespace to Current File — switch the REPL to the file's namespace

Key features:

  • Full autocomplete with Ctrl+Space
  • Inline pretty-printed results in the REPL toolwindow
  • Debug REPL support — run in debug mode and set breakpoints
  • Visual diff of data structures via (cursive/diff a b)
  • REPL commands — create custom keybindings for common workflows (e.g., reload + evaluate)

Calva (VS Code)

  1. Start an nREPL server (bb --nrepl-server or clj -M:nREPL -m nrepl.cmdline)
  2. Ctrl+Alt+C, Ctrl+Alt+P — Calva connects automatically

Loading Your Code

Once connected, load your source files:

;; Load learnclj namespace
(require '[learnclj :as lc])

;; Switch to the namespace
(in-ns 'learnclj)

;; Now all functions are available
(greet "Sam")
;; => "Hello, Sam!"

Resources