What are the alternatives to using remove-ns to clean up definitions

#6

refresh.clj in cider-nrepl looks relevant.

1 Like
#7

OMG. I already have these ops prepared in the Calva code. Just need to make them available as commands.

#8

That feeling when you get more traction with code than you expected, and not the other way :grin:

1 Like
#9

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?

1 Like
#10

Nice!

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.

1 Like
#11

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…

3 Likes
#12

But you still use refresh? Just checking.

#13

I can’t believe I never knew about cider-refresh… wow!

1 Like
#14

No, no refresh, no refresh all. I just don’t seem to need them.

1 Like
#15

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.

#16

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).

2 Likes
#17

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.

#18

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.

1 Like
#19

Yes, we use Component very heavily. No, we don’t use its reset function.

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.

7 Likes
#20

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?

1 Like
#21

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.

1 Like
#22

Neat trick.

I guess (def bla nil) works for all defonce Var?

1 Like
#23

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 ns-unmap it.

(it was a good question – I had to go experiment in the REPL to figure out the answer!)

#24

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.

1 Like
#25

I use 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.

2 Likes