refresh.clj in cider-nrepl looks relevant.
OMG. I already have these ops prepared in the Calva code. Just need to make them available as commands.
That feeling when you get more traction with code than you expected, and not the other way
Now my poor beta testers can test refresh and refresh-all. It only refreshes the clj repl right now. Do you know if CIDER supports refresh for cljs repls?
As far as I know, the default Duct template makes use of refresh for browser REPLs, and refreshing from the editor then reloads cljs code as well.
FWIW, I’ve never needed any sort of “refresh all” in my workflow. I have a REPL running with all the dependencies of our monorepo across a dozen or so subprojects, and it runs for days and days and days. I probably restart it once a week at most…
But you still use
refresh? Just checking.
I can’t believe I never knew about
refresh all. I just don’t seem to need them.
That’s interesting. Have you written about this somewhere? Even if your style of coding is not for everyone, all the time, it seems I could learn a lot from getting to know more about it.
I honestly don’t think I’m doing anything different – I just don’t seem to need a “refresh”. I wonder if folks have rushed to adopt it without waiting to see whether they experience pain without it?
No refresh is mentioned anywhere in the clojure.org https://clojure.org/guides/repl/introduction pages (I just checked). Pretty sure I’ve never heard Stu Halloway mention/show it in his various REPL usage talks? (just searched the https://github.com/matthiasn/talk-transcripts/tree/master/Halloway_Stuart transcripts repo and the only
refresh is in one of David Nolen’s talks in a completely different context).
Interesting to hear that you’re not using it. There is Component, though, and the related
component.repl/reset, which does something similar. I’m left with a few loose threads, though,
- Do you use a component system like Component or Integrant? If not, how do you manage stateful components, and the need to reload / refresh those?
- Do you ever run into problems with multimethods?
- Do you ever run into ordering problems in namespaces? I’ve had instances where I just define and redefine things in the REPL, and suddenly when I come back after a REPL restart, I realize that I’ve used some var before defining it.
James Reeves (Ring, Component) seems to be influenced by reloads / refreshes in his work. See Advancing Duct.
To be clear, I’m curious, not trying to argue.
I also mostly don’t use refresh. Though sometimes it is useful. So occasionally I do need it.
I think with regards to components, what component really need to be refreshed? That’s mostly necessary if you change the config for it. So normally, I don’t need to do that.
For example, my database doesn’t really need refreshing. Create the connections on start, close them on stop. But once I have it, I can just modify my query code and call it on the connection.
So I guess I just never had components that constantly need to be restarted.
For multi-methods, well, first, I don’t use them a lot to begin with. Also, only the defmulti has to be refreshed. The defmethods don’t need to be (from my memory). So I guess, it is less often I need to change the defmulti and more often I need to change the defmethods.
For ordering, I think this is my biggest use of it. Or actually, more when I have changed things in multiple namespace and don’t remember the require order. That said, you can also often just trust yourself and manually re-eval the namespaces.
Yes, we use Component very heavily. No, we don’t use its
No, I don’t run into problems with multimethods: you can use
(def multi-fn nil) to remove a multimethod definition so it can be predictably redefined (putting that above your
defmulti should be enough).
No, I don’t run into problems with ordering in namespaces. I suspect that’s a combination of 8+ years of Clojure experience, being careful to check for first use of functions when I do move things around, and never typing directly into the REPL (I always eval code from a source file, so I use “Rich Comment Forms” for non-production/exploratory code in my source files).
Also, we have a stub test namespace for every single source file so when we run
clj -A:test:runner we get a failure if there’s an ordering problem – and we run our automated tests a lot, locally! We have a script that generates a stub test namespace for any source file that doesn’t already have one: it requires the source namespace and refers all symbols as a baseline for “can this source file be loaded” successfully.
Perhaps another aspect of my workflow that helps with all this: whenever I change any code at all, I always eval the top-level form that it’s in – so the REPL always has the up-to-date version of my code. That’s just habit, but it’s also part of a good REPL-Driven Development workflow: make small changes and always test every change in the REPL as you go. That means that instead of making a slew of changes across multiple namespaces and hoping you got it all correct, you work from the bottom up, testing each changed file as you go.
Thank you! The trick with
(def multi-fn nil) is neat.
How do you avoid the problem of renaming/removing a function and forgetting old references to it? I guess it is the same as with ordering and the stub tests would catch it?
Pretty much, yes. Also, when I rename a function, I always do a (global) find on the old name to check I got all instances (and, of course, I’ll eval each changed form into the REPL as I make edits).
To be honest, I don’t find myself renaming functions very often – and I think that’s partly from “playing” with nearly every function via the REPL as I write them, so the names “evolve” to a stable state before I have calls to them elsewhere. In the same way that TDD tends to guide how you design a function’s name and signature – by focusing on usage, rather than implementation.
I guess (def bla nil) works for all defonce Var?
defonce checks for a root binding and will not overwrite any Var that has one – so, no, the
(def bla nil) trick won’t work with
defonce, and you’ll have to
(it was a good question – I had to go experiment in the REPL to figure out the answer!)
I use a function to walk all the namespaces and remove the mappings to the var:
It’s a bit complicated because it handles both Clojure and Clojurescript. A Clojure only version could be very much simplified.
remove-ns when I really can’t figure out why something isn’t working, just in case it’s an old binding. I don’t know if I’ve ever used
clojure.tools.namespace.repl/refresh. I prefer a minimal toolchain. If I really get stuck, I just restart the REPL. That’s rare. My computer crashes more often than I reset the REPL.
Part of it is laziness. I put
clojure.tools.namespace on my mental “check it out” list and just never got to it. But I guess that shows that I never needed it.
Two big reasons to use a tool like
refresh are multimethods and protocols. If you redefine the protocol, you have to re-eval all
extend-protocol forms, among other issues. But it strikes me that I also rarely use protocols and multimethods, so that may be why I’ve never needed it.