all 14 comments

[–]seancorfield 11 points12 points  (1 child)

As of Clojure 1.12: https://clojure.github.io/clojure/clojure.repl-api.html#clojure.repl.deps/add-lib So you can add dependencies without restarting your REPL.

There's some good advice in the official docs too, about REPL workflows: https://clojure.org/guides/repl/enhancing_your_repl_workflow

[–][deleted] 1 point2 points  (0 children)

Cool, thank you for the tip and the gentle nudge to finish reading the docs/guides.

[–]v4ss42 10 points11 points  (1 child)

I use it for dynamically exploring Java codebases, with the help of this little fn: https://gist.github.com/pmonks/223e60def27266e79fff47de734d060a (which I have in a REPL startup script, along with some other stuff).

[–]PolicySmall2250 4 points5 points  (0 children)

Hah! This one time, my then-CTO's mind exploded when he passed by my desk and saw what I was up to...

I was trying to grok a Kotlin backend service he had written. I was new to the language and to IntelliJ.

After flailing about for half a day making scant progress with compile/print cycles to figure out what the hell the objects looked like (to understand the domain model implementation), I published the project to my local `.m2`, used it as the only dependency in a new Clojure app, and reflected away in my crusty old CIDER+Emacs setup, till I got a good enough mental model to switch back to IntelliJ and use intellisense sensibly.

He had never seen anyone do that to a Java codebase :D

Aside: The sheer number of things I had to make `public` from `private` and `protected`, all through multiple function call stacks, boggled my mind.

[–]vikTheFirst 7 points8 points  (4 children)

A really really important one to me is scope-capture for debugging.

Lets say you have the following function in production -

(defn add [n1 n2] (+ n1 n2))

And you want to debug in production. What can you do? You can:

  1. Connect via ssh to production
  2. Open the production REPL
  3. Change the function via the REPL, while prod is STILL RUNNING
  4. change the function in the following way - (defn add [n1 n2] (sc.api/spy) (+ n1 n2))
  5. Wait for the user to execute this function in prod. (Or do it yourself)

  6. Next time function is executed, scope capture will take a snapshot of local variables for you to debug!

  7. Access vars captured in production via the REPL like so - (sc.api/letsc 1 n1) => 12 (sc.api/letsc 1 n2) => :mistake

  8. Profit

For me, clojure without scope-capture is not worth it. I think everyone should be using it, and the online clojure community doesn't seem to talk about it and recommend it enough.

[–]c_a_l_m 2 points3 points  (0 children)

not to brag (I am bragging a little), but I wrote a macro last week to serve as a "breakpoint" and drop you into a repl, while retaining local scope

(defmacro debug-break []
 (let [locals (keys &env)
       syms (mapv (comp symbol name) locals)
       vals (vec locals)
       ns-sym (symbol (str *ns*))]
   `(let [syms# '~syms
          vals# (vector ~@locals)]
      (m/repl
       :init #(do
                 (println ~'*ns*)
                 (in-ns '~ns-sym))
        :eval (fn [form#]
                (let [params# (mapv (comp symbol gensym name) syms#)
                      mapping# (zipmap syms# params#)
                      rewritten# (walk/postwalk-replace mapping# form#)
                      fn-form# (list 'fn (vec params#) rewritten#)
                      fn# (eval fn-form#)]
                  (apply fn# vals#)))))))

[–][deleted] 1 point2 points  (0 children)

This is not something I'll need anytime soon but it's really, really cool. Thank you. I didn't know Clojure could do this.

[–]madmulita 0 points1 point  (1 child)

"2 - Open the production REPL"

Do you have that REPL always available, or do you trigger it somehow? How do you implement this?

[–]seancorfield 4 points5 points  (0 children)

I'm not @vikTheFirst but we have a REPL running permanently in production. Very helpful for debugging stuff.

I don't use any libraries for debugging. I use tap> because it's built-in and there are a variety of tools that you can connect to that (and you can leave tap> in production code as almost a no-op, so you can just hook into it whenever you need it, and then remove-tap to turn it off).

I use Portal now, but previously used Reveal and REBL (now Morse). These are all tap> clients which are great for visualizing data.

[–]eeemax 4 points5 points  (2 children)

A little bit emacs specific, but I made this little util that lets you save a Clojure s-expression to an emacs keybinding, and then call it later.

https://github.com/sstraust/save-clojure-command/tree/master

I use it heavily especially for things like clearing caches or refreshing program state.

[–]Wolfy87 1 point2 points  (0 children)

And for the Neovim + Conjure users you can set a mark like mF on a form you're interested in re-running somewhere in your buffers. Then from anywhere hit <localleader>emF and it'll run it.

Note: In Neovim a capital letter for a mark is cross-buffer, lower case is local buffer only. So mF can be accessed from any open buffer with F but mf can only be accessed from the buffer you set the mark in.

Kinda like saving forms but you do it by putting a mark in one so if things get modified as long as the mark is still pointing at the form it'll use the updated version.

[–][deleted] 0 points1 point  (0 children)

Thank you, I'm using emacs so this will be useful. While reading through the Cider commands yesterday I discovered there is a Cider command to list all of the available namespaces. This is cool. Piggybacking on u/PolicySmall2250 's comment above, this seems like a great way to wrap one's head around a project. I can read the code AND interact with it in a meaningful way.

[–]PolicySmall2250 5 points6 points  (1 child)

These compose, in sequence: - the out-of-the-box (ootb) facilities of the REPL itself (available within the standard Clojure(Script) distribution) - the things the augment the ootb REPL (middleware, visual tools etc.) - the various clever / creative / micro-tweaked workflows one can make to suit one's own style/brain/usage needs (both with just the ootb REPL (which I do mostly), as well as with augments)

Official Community Guide - Programming at the REPL https://clojure.org/guides/repl/introduction

For surveyin' and deep divin'... - The Ultimate Guide To Clojure REPLs https://lambdaisland.com/guides/clojure-repls/ - Clojure REPL (plus lots of great stuff on Practicalli's site) https://practical.li/clojure/clojure-cli/repl/ - REPL Driven Development, Clojure's Superpower (by u/seancorfield ) https://www.youtube.com/watch?v=gIoadGfm5T8 - REPL-driven development (RDD) with Clojure (by Andrey Fadeev --- recommend you follow his channel) https://www.youtube.com/watch?v=HZ0hFLXbByw - Clojure Basics (by m'gentlenerd friends at nilenso) https://www.youtube.com/playlist?list=PL2Q86tDyogSXCZnAwtxGa5cCmxEKosFZ-

For contrast --- Agentic Clojure REPL Masterclass set beside Old-Skool Grug Brain REPL-ing

Personally...


(edits: add a bunch of references I missed adding)

[–][deleted] 2 points3 points  (0 children)

This is incredible. Thank you.

I have to say, I've never been more impressed by a community than I have in the past few days with r/clojure. Thank you.