I can group my work rougly into two categories: finding the right transformation to produce a desired data structure, and figuring out how to use a library that someone else has written.
For the latter case, I start by setting up an environment where I use the library. On a REPL prompt in a Terminal window I’ll try to call the function, often with incomplete arguments. I’ll look at what it returns. If it returns the wrong result or throws an exception, I see if I can fix that particular problem. A side benefit is that I know how the function fails if it does (silently? throw? incorrect results?).
I proceed step by step to get some idea of what parts the library is composed of. Sometimes fixing problems requires passing in more complete arguments. In such cases I’ll write a function that produces test data (
make-customer or the like). Sometimes I’ll store that data in a var. But I tend to use as little state as possible, as state makes it easier to reproduce results.
Hopefully I’ll eventually arrive at something that works. The next step is to start from a clean slate and to rerun the experiment. This step is easy in figwheel (reloading the browser window) and lumo (simply restart the repl). In Clojure restarting the REPL takes longer, which annoys me. More often than not, the series of forms I entered won’t work exactly as expected because it previously relied on lingering state so I need to re-define vars or change names.
Once I can reproduce the functionality reliably, I’ll copy the forms into an Emacs buffer visiting a Clojure(Script) namespace. Often this is a
user.clj(s) - essentially a long file serving as an extension of my terminal REPL. I’ll stick the code in functions and try to give them sensible names. If I can’t come up with a good name, I’ll call the function “banana” and rename it later. Now I need to send the code to the REPL again. With Figwheel, that happens automatically on save; in Clojure I use
cider-eval-buffer or its Unrepl.el equivalent. Code eventually graduates from
user.clj(s) to a proper namespace.
The process for data transformation functions is similar, but I often start with a threading macro. Starting from
(->> ["a.txt" "b.txt"]), I’ll add transformation steps. Because multiline editing is inconvenient, I usually try to keep things in one line. I use
Cursor Up) to recall the previous line to fix issues or extend the pipeline. At each additional step, I make sure I get immediate feedback.
Again, once things start looking good, I’ll clean up the code, copy it to Emacs, fix the formatting, introduce
lets and commit a “WIP” checkpoint to git. For testing, I return to my separate Terminal window an rerun the function from there. This is convenient because I usually just press
Cursor Up to recall the previous testing command. I can also easily compare the result to the output of previous commands.
As you can see, this workflow doesn’t make heavy use of Emacs features for REPL interaction (though I use paredit, smartparens etc. extensively). In a nutshell, I live in the Terminal REPL; I eschew Emacs’s various terminal emulator offerings in favor of iTerm2 on macOS and gnome-terminal on Linux.
I’m fond of this workflow, because it keeps the cognitive load manageable and does not depend on a complicated tooling setup. All I need is a Terminal prompt provided by unravel, lumo or figwheel. I do require a prompt with full Readline functionality (like rlwrap) as I rely heavily on
^R to search my readline history for words.