Efficient Clojure workflow?
Asked Answered
T

2

28

I am developing a pet project with Clojure, but wonder if I can speed up my workflow a bit.

My current workflow (with Compojure) is:

  1. Start Swank with lein swank.
  2. Go to Emacs, connect with M-x slime-connect.
  3. Load all existing source files one by one. This also starts a Jetty server and an application.
  4. Write some code in REPL.
  5. When satisfied with experiments, write a full version of a construct I had in mind. Eval (C-c C-c) it.
  6. Switch REPL to namespace where this construct resides and test it.
  7. Switch to browser and reload browser tab with the affected page.
  8. Tweak the code, eval it, check in the browser.
  9. Repeat any of the above.

There are a number of annoyances with it:

  1. I have to switch between Emacs and the browser (or browsers if I am testing things like templating with multiple browsers) all the time. Is there a common idiom to automate this? I used to have a JavaScript bit that reloads the page continuously, but it's of limited utility, obviously, when I have to interact with the page for more than a few seconds.
  2. My JVM instance becomes "dirty" when I experiment and write test functions. Basically namespaces become polluted, especially if I'm refactoring and moving the functions between namespaces. This can lead to symbol collisions and I need to restart Swank. Can I undef a symbol?
  3. I load all source files one by one (C-c C-k) upon restarting Swank. I suspect I'm doing it all wrong.
  4. Switching between the REPL and the file editor can be a bit irritating, especially when I have a lot of Emacs tabs open, alongside the browser(s).

I'm looking for ways to improve the above points and the entire workflow in general, so I'd appreciate if you'd share yours.

P. S.

I have also used Vimclojure before, so Vimclojure-based workflows are welcome too.

Tsushima answered 6/6, 2010 at 12:27 Comment(0)
H
19

Not a complete workflow description, just a few ideas:

  1. It is possible to remove a Var from a namespace with the ns-unmap function. For added convenience, an undef macro can be built on top of it e.g. like so:

    (defmacro undef [& syms]
      `(do ~@(map (fn [s] `(ns-unmap *ns* '~s)) syms)))
    

    ns-unalias may also be of interest.

  2. There's no reason to go through the files holding the namespaces just to do C-c C-k in each of them; you can just require the namespaces you need at the REPL.

    Moreover, if you type a few characters at the SLIME REPL and then use M-p / M-n to browse history, only the entries matching the initial bit of text you entered by hand will be displayed. This is compatible with Paredit (the trailing closing bracket(s) will not be a problem). So, if you build up a huge require at the start -- (require '[foo :as f] '[bar :as b] '[clojure.contrib.sql :as sql] ...) -- then after restarting Swank, all you need to do is to type something like (require '[f and press M-p to bring that form to the REPL prompt again.

    Admittedly, this could be automated in a number of ways (e.g. having the Swank REPL search for a configuration file, or perhaps a simple macro expanding into an appropriate require form which could be used after bringing in just one utility namespace from the project -- especially the latter idea would be easy to implement), but I find it sufficiently low on the annoyance factor that I haven't so far bothered with any improvements.

  3. You can use C-c C-z to make a window with the SLIME REPL pop up while you are in a SLIME-enabled buffer. Also, you should try using ido if you haven't already. I tend to work with a code buffer open in a window on the left hand side of the screen and a REPL buffer on the right; with windmove-left and windmove-right bound to some convenient keys, I tend to be pretty happy. If I need to look at additional buffers often, I use extra Emacs frames.

  4. Incidentally, I don't use lein swank normally, I prefer my custom clojure-project function (a tweaked version of Phil Hagelberg's original). On occasion, I feel a desire to improve it... perhaps I'll deal with per-project import / require automation next time that happens. ;-)

Higa answered 6/6, 2010 at 14:26 Comment(0)
L
5

I'm not sure about Lein, but in Maven you could specify name of the repl script (replScript config param), that allows to specify which commands will be executed on start of REPL & Swank... And if you have separate namespace for your project, you could use functions from clojure.contrib.find-namespaces to find your namespaces and load them all....

Laminated answered 6/6, 2010 at 15:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.