add RDD breakdown
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
# teach
|
||||
|
||||
Clojure learning materials and examples.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [learnclj.clj](learnclj.clj) — Comprehensive Clojure reference with examples covering basics to advanced topics
|
||||
- [repl.md](repl.md) — Guide to REPLs, starting a Babashka nREPL server, and REPL-driven development
|
||||
|
||||
## Files
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| `learnclj.clj` | Clojure learning reference covering literals, data structures, control flow, destructuring, protocols, records, state, Java interop, threading macros, and more |
|
||||
| `repl.md` | Explains what a REPL is, how to start a Babashka nREPL server, and how REPL-driven development works |
|
||||
@@ -0,0 +1,212 @@
|
||||
# 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:
|
||||
|
||||
```clojure
|
||||
;; 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.
|
||||
|
||||
```bash
|
||||
# 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)
|
||||
|
||||
```bash
|
||||
clj -A:nrepl
|
||||
```
|
||||
|
||||
This starts a Clojure REPL with nREPL support. For a server:
|
||||
|
||||
```bash
|
||||
clj -M:nrepl-server
|
||||
```
|
||||
|
||||
### Connecting from Your Editor
|
||||
|
||||
#### Calva (VS Code)
|
||||
1. Start an nREPL server (`bb --nrepl-server` or `clj -A:nrepl`)
|
||||
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 -A:nrepl`)
|
||||
3. `:ConjureConnect localhost <port>` — connect to the REPL
|
||||
4. Send code with `,e` (evaluate current line/form)
|
||||
5. Results appear inline below the code
|
||||
6. Open the REPL buffer with `:ConjureShow`
|
||||
|
||||
**Key Conjure shortcuts:**
|
||||
- `<localleader>ee` — evaluate form under cursor
|
||||
- `<localleader>E` — evaluate current line/buffer
|
||||
- `<localleader>pp` — evaluate and print (with pprint)
|
||||
- `<localleader>dd` — evaluate and doc
|
||||
- `<localleader>oo` — open result in browser (for HTML, URLs, etc.)
|
||||
- `K` — doc word under cursor (built-in, no prefix)
|
||||
|
||||
> 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 -A:nrepl`)
|
||||
2. `Ctrl+Alt+C, Ctrl+Alt+P` — Calva connects automatically
|
||||
|
||||
### Loading Your Code
|
||||
|
||||
Once connected, load your source files:
|
||||
|
||||
```clojure
|
||||
;; Load learnclj namespace
|
||||
(require '[learnclj :as lc])
|
||||
|
||||
;; Switch to the namespace
|
||||
(in-ns 'learnclj)
|
||||
|
||||
;; Now all functions are available
|
||||
(greet "Sam")
|
||||
;; => "Hello, Sam!"
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
- [Babashka Docs](https://babashka.org/)
|
||||
- [Clojure REPL Guide](https://clojure.org/guides/repl/app_dev)
|
||||
- [Calva](https://calva.io/) — VS Code extension for Clojure
|
||||
- [CIDER](https://cider.readthedocs.io/) — Emacs Clojure IDE
|
||||
- [Conjure](https://github.com/Olical/conjure) — Async REPL for NeoVim
|
||||
- [vim-fireplace](https://github.com/tpope/vim-fireplace) — Vim Clojure integration
|
||||
- [Repl-Driven Development](https://nakkaya.com/2019/03/12/repl-driven-development/) — detailed article on RDD
|
||||
Reference in New Issue
Block a user