A REPL?! What is it?
REPL stands for Read-Eval-Print Loop, and in Clojure it’s the way to work with
code. It’s interactive, fast, and honestly just fun. You write some code, run it
immediately, and boom - you’ve got the result right there.
The problem with the default NodeJS REPL is that it’s not very practical for
everyday coding. Running it in a terminal isn’t convenient: you don’t see the
full picture, your code isn’t linted/checked beforehand, and you’re constantly
switching between the editor and the terminal, losing focus and context.
So, in this post I’ll show you how to make JavaScript coding way more
interactive and enjoyable - kinda like how people code in Clojure.
For example, in JavaScript you might write:
function add(a, b) { return a + b; }
To test it, you’d usually add some tests, or do something like:
console.log(add(36, 6));
Then run it with:
node index.js
That’s a lot of steps for a single result.
In Clojure, it’s much more direct:
(defn add [a b] (+ a b))
(add 35 7) ;; => 42
The feedback loop is immediate. (Of course, we still need proper tests for
real-world software, but for exploration and problem-solving, a REPL is very
effective.)
The Recipe
So how do we bring that to JavaScript?
I wrote an extension for the Conjure plugin
in Neovim. Setup is simple - just follow the
docs:
- Install Neovim
- Install Conjure
- Install NodeJS
- Install Treesitter and
run
:TSInstall javascript
inside Neovim - Create a new ".js" file and open it in Neovim(for example: nvim repl-test.js in your terminal). Conjure should attach automatically if everything is setup correctly. That’s it - you’re ready to start evaluating JavaScript straight from your editor.
Why bother? Because once you’ve got this REPL loop going, you make fewer bugs,
you can actually feel the flow of your code, and you tweak things way faster.
Example:
const mult = (a, b) => a * b;
mult(3, 4); // (out) 12
mult(5, 3); // (out) 15
mult(10, 8); // (out) 80
Want to see the value of an expression quickly? Press <localleader>ee
(Conjure’s default). Need to explore an object? Just evaluate it:
const obj = { a: 1, b: { c: 3 } }; // eval this first
obj.b.c; // (out) 3
Debugging on the Fly
Checking intermediate steps becomes simple:
[1, 2, 2, 3].reduce((acc, x) => {
console.log("acc", acc);
console.log("x", x);
console.log("val", acc[x]);
return { ...acc, [x]: ++acc[x] || 1 };
}, {});
You’ll instantly see each step in Conjure’s log buffer. No rerunning files, no
juggling terminals. Just... flow.
A Quick Clojure Example
Clojure developers often define a function and immediately test it inside a
(comment ...)
block:
(defn name-extractor [m] (get m :name))
(comment
(name-extractor {:name "Kisha"}) ;; "Kisha"
(name-extractor {:age 20})) ;; nil
If we want to return :noname
when the field is missing, we just adjust the
function:
(defn name-extractor [m]
(get m :name :noname))
And if we want to handle empty strings as well:
(defn name-extractor [{:keys [name]}]
(or (not-empty name) :noname))
Each step is tried and refined in the REPL.
Doing the Same in JavaScript
function nameExtractor({ name }) {
return name || "noname";
}
nameExtractor({ name: "John" }); // 'John'
nameExtractor({ age: 15 }); // 'noname'
nameExtractor({ name: "" }); // 'noname'
Just like in Clojure, you can write small test cases inline and adapt the
function step by step.
Exploring RXJS
RXJS can feel abstract at first, but with the REPL you can experiment
interactively and see results as you go:
import { BehaviorSubject } from "rxjs"
import { combineLatestWith, map, tap } from "rxjs/operators"
let o1 = new BehaviorSubject({ a: 1 })
let o2 = new BehaviorSubject({ b: 6 })
o1.pipe(combineLatestWith(o2),
tap(x => console.log("combined:", x)),
map(([{ a }, { b }]) => a + b),
tap(x => console.log("sum:", x))
).subscribe()
o1.next({ a: 2 })
o2.next({ b: 8 })
o1.unsubscribe()
o2.unsubscribe()
You can adjust values, rerun them, and instantly observe how the transformations
behave.
Useful Keybindings
-
<localleader>ef
- evaluate the whole file -
<localleader>ts
- toggle Stray Out Mode (to see delayed evaluations likesetTimeout
) -
<localleader>cr
- restart the REPL (clears all previous evaluations) -
<localleader>E
- evaluate a selected block of text in visual mode (great for JSDoc examples)
NodeJS Quirks (Handled by Conjure)
- Arrow functions can’t be redefined in the NodeJS REPL, but Conjure rewrites them as regular functions.
-
import
isn’t supported in the NodeJS REPL, but Conjure translates it intorequire
.
Wrapping Up
These tricks can make your workflow in JavaScript faster, more interactive, and
closer to the Clojure REPL style. A shorter feedback loop not only makes coding
more enjoyable, but also helps you write cleaner, more reliable code.
Top comments (1)
Wow, this hits the sweet spot, I’ve always wanted JS to feel more interactive without jumping between editor & terminal all the time. Using Conjure + Neovim for REPL sounds great.