This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-02-12
Channels
- # announcements (32)
- # aws (7)
- # babashka (2)
- # babashka-sci-dev (1)
- # beginners (25)
- # biff (1)
- # calva (1)
- # cider (27)
- # clj-kondo (15)
- # clojure (24)
- # clojure-berlin (1)
- # clojure-czech (4)
- # conjure (9)
- # cursive (7)
- # datalevin (1)
- # emacs (19)
- # events (1)
- # gratitude (1)
- # integrant (1)
- # introduce-yourself (2)
- # java (4)
- # meander (17)
- # membrane (4)
- # podcasts (1)
- # releases (1)
- # remote-jobs (2)
- # ring-swagger (8)
- # shadow-cljs (14)
- # testing (1)
- # tools-build (5)
- # tools-deps (3)
I’m having a hard time understanding the relationship between a REPL started with clojure.core.server
and the REPL that I see in my terminal with the clj
command. Here’s a minimal reproduction of what I’m working with:
(ns user
(:require
[clojure.core.server]
[integrant.core :as ig]
[integrant.repl :refer [go reset reset-all]]))
(defmethod ig/init-key ::clj-repl [key opts]
(clojure.core.server/start-server (assoc opts :name (name key))))
(defmethod ig/halt-key! ::clj-repl [key _]
(clojure.core.server/stop-server (name key)))
(integrant.repl/set-prep!
(constantly
{::clj-repl
{:accept 'clojure.core.server/io-prepl
:address "127.0.0.1"
:port 6777
:server-daemon false}}))
I start up Clojure with clj
, and I’m prompted with a REPL. I initialize my Integrant system.
Clojure 1.10.3
user=> (go)
init clj-repl
:initiated
user=>
In Emacs, I connect with inf-clojure to localhost:6777
. Evaluation works well. Through inf-clojure, I evaluate:
(def foo "bar")
… resulting in the expected prepl response:
{:tag :ret, :val "#'user/foo", :ns "user", :ms 0, :form "(def foo \"bar\")"}
And intuitively, I can now go back to my Terminal REPL (started with clj
) and evaluate foo
:
user=> foo
"bar"
user=>
Seems like the io-prepl
and clj
are sharing the user
namespace. Makes sense, and the behavior that I was hoping for! However, when I reload with Integrant:
user=> (reset-all)
:reloading (system.main user)
:resumed
user=>
…I evaluate foo
again. It errors. This is also what I anticipated from the use of reset-all
.
user=> foo
Syntax error compiling at (REPL:0:0).
Unable to resolve symbol: foo in this context
user=>
However, back in Emacs + inf-clojure, I can continue to evaluate foo
with my io-prepl
connection…
(do foo)
…and it still evaluates to "bar"
. No error.
{:tag :ret, :val "\"bar\"", :ns "user", :ms 0, :form "(do foo)"}
The io-prepl
the clj
terminal REPL are both in the user
namespace… but they’re clearly out of sync. This problem continues with every use of reset-all
.
It seems that io-prepl
is somehow keeping its own state, which isn’t getting reset with the use of clojure.core.server/stop-server
. What confuses me most is that it was initially in sync with the clj
terminal’s REPL state. After reset-all
, they’re out of sync, and each one seems to keep a state of its own.
Has anyone seen this before?But names, vars, in namespaces are global, in a clojure runtime, and any repls will see the same state
My guess as to how you are mistaken is your repls are not connected to the same clojure process
That’s the part I don’t really understand. It’s just one Clojure process, started with clj
. And the REPLs are in sync until I call reset-all
I think similar kinds of things are possible, but exactly what you described is not, so are you describing exactly what you see, or summarizing/editorializing something more complicated?
E.g. if you hang on to a var object it doesn't magically get replaced with the new var when you reset (which just uninterns all the vars so you get new var objects)
In theory if the reset code maybe un-interned the namespace object itself, maybe you could get something like you've described, but the code reset all calls doesn't do that (ultimately calls this stuff https://github.com/clojure/tools.namespace/blob/master/src/main/clojure/clojure/tools/namespace/repl.clj#L67) and i just think on theory you could, I think it is likely, in the scenario you described, both repls still wouldn't behave like that
@neil.hansen.31 I think all the reset/reload/refresh workflows are extremely problematic and generally recommend people avoid them. They have weird corner cases and often very counter-intuitive behavior -- and they are simply not necessary if you develop a good REPL-friendly workflow. As to the specific problems you are running into, I'd suggest asking in #integrant because you're doing some stuff that is very specific to that project.
@U04V70XH6 Thanks for the perspective. I actually thought the reset/reload style was the standard REPL workflow. Can you suggest any resources about the style you suggest?
@neil.hansen.31 Sean's fully capable of answering himself, but… here's a great talk he gave on this very topic 😅 https://m.youtube.com/watch?v=gIoadGfm5T8
Also, Stu's excellent talk from a few years prior -- here's a link to the transcript if you prefer to read (as I do), which in turn links to the video on Vimeo: https://github.com/matthiasn/talk-transcripts/blob/master/Halloway_Stuart/REPLDrivenDevelopment.md
If you're willing to spend money on a course to teach you about this sort of workflow, consider Eric Normand's REPL-Driven Development course: https://purelyfunctional.tv/courses/repl-driven-development-in-clojure/
(I have taken that course and highly recommend it to everyone!)
@U04V70XH6 @UGTAV6LR2 These are perfect resources, thanks for your help.
How often do you use https://clojuredocs.org/clojure.core/remove-ns ? Would like to recommend it to those who get themselves into repl difficulties but it looks like it leaves corner cases too.
@U3TSNPRT9 I don't use it at all because it can break code that depends on certain things in the ns being removed. I occasionally use a snippet of code that removes refers, aliases, and interns from a ns: https://github.com/seancorfield/vscode-clover-setup/blob/develop/config.cljs#L83-L89
That cleans out the "inside" of an ns so you can do "load file" and get a pristine version without breaking any dependencies (because the ns object itself does not go away).
Note that even (require ... :reload)
can break dependencies on an ns sometimes (you'll note I have code in that file to (require ... :reload-all)
but I almost never use that because it tends to break dependencies) -- I keep meaning to remove it!
@U04V70XH6 Thanks - feel like there should be a "clean namespace" button/shortcut/menu item running this snippet in IDE integrated REPLs (cc @U0567Q30W 🙂). It would help those newbie co-workers who restart the REPL constantly and find it "unreliable" because redefining vars and aliases everywhere is more side-effectful than they realise.