Fork me on GitHub
Noah Bogart01:10:07

Hey friends, I have an uberjar running. I want to open a repl to it. Is that possible?


@nbtheduke Not from the outside. You need to either a) start the uberjar with JVM opts to tell Clojure to start a Socket REPL or b) program something into the app to explicitly start up some sort of REPL.

👍 4
Noah Bogart01:10:25

Good to know, thank you


If you don't mind restarting the app, you can just provide the JVM option to start a Socket REPL. We run local Socket REPLs in a lot of our processes and then tunnel into the server to get access to that.

Noah Bogart01:10:39

Maybe I’ll try that next time I restart the app!


@seancorfield do yall take any steps to ensure that your socket repl access is "read only" in the sense that you don't redefine anything important?


or just kinda scouts honor?


We use those Socket REPLs to apply live patches to the running processes sometimes 🙂


I thought I remembered reading a blog of yours that you used to do that but had turned against it lately


Someone has to be able to ssh into the server to get at the REPL...


...We stopped running nREPL servers in processes. We didn't want the dependencies added.


(We used to run nREPL servers with full CIDER middleware enabled... and that was... a bit much... but a Socket REPL is built right in to Clojure so no deps needed for that)


We have an optional per-process dot file that the service script picks up so we can decide which processes get a REPL and what port it is on (and any other JVM option level stuff we want, when we want it).

Noah Bogart02:10:42

How do you set this up? Do you have a blog post detailing?


Ya, that's why I'm a fan of unrepl


I can't wait for the time nRepl can bootstrap itself from a socket repl


I just don't get the enthusiasm for nREPL...


...I switched from Atom/ProtoREPL (which required a server-side dependency that included nREPL / Compliment etc) to Atom/Chlorine about a year ago, and I've really enjoyed not needing any server-side dependencies, and only minimal code loaded via unrepl (and I sort of wish Chlorine didn't even use that).


I thought it was a good day when Chlorine made Compliment optional recently -- and built its own simple auto-complete (that works just fine for me).


I suspect if I was still using Emacs, I'd be a big fan of inferior mode or whatever the simplest possible package that can connect to Socket REPL 🙂


I don't know what features are provided by nRepl. But what I do need is auto-complete, go to definition, debugging, pretty print, interrupt, run test, etc.


What "debugging" do you rely on?


Cider debug


It's pretty amazing


I have no idea what that does.


It instruments a form or function, and then it can break, continue, in, out, inspect, trace, etc as you see fit


Hmm, I've never cared for that sort of thing much, in any language TBH.


I've stopped having to add my own print lines or def since it's so convenient


Well, in other languages it's pretty annoying


But in Cider its integrated as part of the repl


I guess I've never felt the need. I like having tap> available built-in (and I use REBL alongside Atom) but really I'm more in Stu's camp of simple tools...


I've had to use breakpoint debugging a few times during my years of Java (and years of C++ before) but that was because the language/tooling was otherwise just so awful.


I really liked Stu's debugging example where he redefined the exception handler to just print "boom!" and provide no info at all 🙂


It's not that you need it. And there's a lot I'm not a fan of with cider defaults. But that debug feature honestly is awesome. I feel it's a little unknown secret though

☝️ 8

It just means instesd.of taking 5 to 10 minutes to add prints and defs and try/catches manually and all, it now takes me 1min to debug the same


I don't know if that means it needs nRepl and all its dependencies though


I actually have inf-clojure and unrepl as well that I use in my emacs, because I don't think the issue is necessarily simple in the sense of featureless. But what cider lacks is simplicity in setup and usage


I think part of my fundamental objection to that level of intrusive debugging -- breakpoints -- is that you can't use it on a production app while it is running, whereas you can use the tap> (or println) approach...

💯 4

Hum... well you could use cider-debug in prod, but you probably shouldn't, since it'll pause your thread


huh, til tap. seems nice. seems like it would mesh very well with something like


For prod I wouldnt say I need all these things. Mostly goto, auto-complete, and doc would be nice for a prod repl. I like to use rebel-readlines in prod actually. I just ssh and start a rebel-readline repl


See, I like to be able to use the exact same tools and approach no matter where the app is that I'm working on / debugging.


That way I won't accidentally use the "wrong" tool in the wrong environment 🙂


I see. Yes I know people who value that a lot.


In my case, I'm not allowed to open remote ports anyways to prod, and I can't ssh tunnel either 😔


So rebel-readline is an amazing alternative


I remember you talking about your work environment a bit... it sounded pretty corporate and locked-down... I'm surprised somewhere like that even uses Clojure 🙂


“it runs on java” cures many ills 😆

👍 8

Hah... Neal Ford's sneaking Clojure in through the back door stuff...


(it was Neal Ford, right?)


Yeah, he has this "we use Java with the Clojure library" schtick


Well, hey, all it takes for a Java app is adding Clojure as a dependency, add a JVM option, and -- bingo! -- you have a live REPL into your legacy/enterprise Java app 🙂


(after all, that's how come we have a REPL into our legacy ColdFusion apps! 🙂 )

David Rueda07:10:34

Sounds cool, which JVM option do you add?

David Rueda09:10:27

That’s awesome, thank you!

👌 4

Btw. this might be useful when connecting to a socket repl running on a machine inside a private network available only via "bastion" host (e.g. on AWS):

# run in separate window
ssh -N -L 55550:localhost:5555 -J [email protected] [email protected]

# then use unravel to connect
unravel localhost 55550

👍 4
Mehdi H.11:10:12

Hi everyone! Would someone with experience on working with pedestal.vase and datomic-pro be available for the assistance I require?

Mehdi H.11:10:08

I am trying to use vase standalone with datomic-pro. The thing is I can't seem to find the latest uberjar online at the link of the doc, and anyway it would be built for datomic-free. I tried to git clone the current github repo but it doesn't "lein uberjar" well even as is. I went back to the 0.9.2 release, commented out the datomic-free dep and added the datomic-pro (with the version I have a licence key to). lein uberjar suggested I excluded, and then the uberjar was completed. But now I am thinking I can't use a .fern config, and I am wondering whether I am missing a way simpler option for all this...


^ You might wanna ask in #datomic

Mehdi H.11:10:28

Thanks man yeah haha that makes a lot of sense. I am new to clojurians slack, didn't even explore the channels available apart from the automatic ones

Shuai Lin11:10:44

Hi, I have question: Any mature open source system to learn about writing a system in clojure? (I posted on reddit, but think may get additional answers here)

Watching the talk "The Language of System" () inspired me to learn practical clojure programming, by studying the source code of some open source systems written in clojure. 

My criteria for such systems are:

* Not a library, not a framework, not a command line tool - but a full-fledged system or application. This is because designing/implementing a system has many unique considerations, e.g. a command line tool can simply abort on error, but a real-world system has to bear with it.
* stands to the tests of real world usage (no research/toy projects even if it has lots stars on github)

For other languages it's easy to find lots of candidates:

* python: zulip, openstack, ansible, etc.
* go: tons of CNCF projects (docker, kubernetes, etc)
* java: tons of apache projects (hadoop, hbase, zookeeper etc.)
* c/c++: countless of open source applications (xwindow, chromium, etc.)

However for clojure it's hard to collect candidate projects, maybe due to me short history with clojure itself, here are a few:
* apache storm (another project from the same author )
* metabase ()

Do you have any suggestions?


@UNMKEJQ1E I've just replied to the post. Thanks for raising this question

Shuai Lin15:10:27

Seems circleci's frontend is a good candidate for learning large-scale real-world cljs applications, but it's not updated for two years, anyone knew what happended to it?


In a recent REPL podcast they mentioned they are moving to JavaScript/React: For various reasons, I think mostly community/tooling (React/JavaScript just has a lot more support) and hiring

Shuai Lin01:10:40

Thanks for the info @U06BE1L6T!


sorry, i was only engaged with circleci for a short time and don’t have much insight in to what happened after i gave that talk. you could try reaching out to some of the (much bigger than me!) contributors, or on twitter


Hi, can anyone let me know how to suppress output when I use swap!


What kind of output?


so when I use swap! to assoc some items into an atom that is a map, the contents of the atom are printed in the repl and also when i run it through lein run


Something to keep in mind is the function passed to swap! may be called multiple times, so any side effects (like output) you don't want happening repeatedly shouldn't be in that function


There is nothing about swap! that prints things


(def k (atom {})) => #'lisp-interpreter.core/k (swap! k assoc 😛 10) => {:b 10}


this is just swap! returning the value


You are using a repl


Read eval print loop


It prints the result


I ran the -main function using lein run and it still prints the same thing to the std out


For any Clojure expression that has a result returned that you do not want printed in a REPL, you can do something like (def tmp <expression>) where tmp is some var you don't care about


I tried do with the last exp as nil


(do whatever nil)


but now it prints nil so I've to add another check


What does your code in -main look like?


You may have some annoying lein plugin breaking things


Your -main function is implementing your own custom REPL, it appears?


@andy.fingerhut your solution certainly will works but is there is a better solution?


Are you saying you don't want it to do the (println (pr-str res)) part, and removing that code makes it do what you want? I'm confused what printing output you are seeing that you want, vs. what you do not want.


Yeah tried writing my own lisp interpreter as learning project


I like (nil? expr) because it’s very short and tells me something that’s sometimes useful


@andy.fingerhut and @hiredman thanks for the help, I think I'm good for now with the suggestions


oh but (= expr) is even shorter!


I might be misinterpreting but

user=> (= nil)


yeah it just always returns true. so not as informative as nil?


it can't provide information because it returns true for all arguments


is shorter isn't it?


You can always define a tiny macro whose name is a single letter long that expands to whatever you want, if you are trying to save characters of typing, e.g. expanding to (do expr nil) or (let [res expr] (nil? ret))


Is there anyway to use define the keys in a spec outside of the spec and then evaluate the value inside the spec? Ex: (def possible-keys [:all :the :keys]) (s/def ::special-map (s/keys :opt-un possible-keys)) That obviously doesn't work because macros, but I was wondering if there was a (preferably idiomatic way to get around that).


one of the goals of spec2 is stuff like this I believe


So I take it that means it won't be easily possible?


You need a multi-spec


I'm guessing you have a map whose keys depend on something else correct?


not easily no. you could write a macro to do it. also there is merge for merging map specs together if that’s what you’re looking for


Not really. I'm defining a spec for a config map. The config comes from several parts is the environment map, and I need to filter that map to have only keys valid for the spec, so I need access to what keys are defined in the spec


I'd hope to have a vector storing the valid keys, which I would then pull from for both the filter and the spec


So the keys for special map are static and known ahead of time?


Okay, then there's two ways I think you can do it


The first I'd recommend is the use of s/describe


Specs are data, but parsing them is a bit annoying


But it's easy, just a bit ugly


By calling describe on ::special-map you get the spec back as a list


And from that you can grab the req vector back


I did this a few times for auto-mapping one map to another


So I'd say s/describe would be the idiomatic way


Ok, that would work. Just wanted to make sure that there wasn't a better way. Thanks!


The other way I forgot if it works with macros, but its worth a try. I believe if you declare the Var which holds the vector as constant


It gets replaced before macroexpansion


But I might be wrong


Another kinda hacky way is to use the read-eval to grab the vector. Using #=


Ok, thanks for the suggestions!


And the last way I can think of is to wrap s/keys in your own macro :p


But I'd say s/describe is the less hacky in my opinion

Alex Miller (Clojure team)20:10:53

I don't know what you're talking about with s/describe, and #= is not a publicly supported feature so I would definitely not recommend that


Am I confused about s/describe returning the spec as a list?


Pretty sure that's what I used before for spec introspection

Alex Miller (Clojure team)03:10:49

well getting the keys is the easy part (should really use s/form though)

Alex Miller (Clojure team)03:10:58

making the new spec is the hard part


If I understood the issue properly, I think they can make the spec, and just grab the keys from it to filter out the proper values from the config. So there's not actually a need for dynamic spec creation in this case.

Alex Miller (Clojure team)19:10:37

you can do it by using either a macro or eval

Alex Miller (Clojure team)20:10:43

user=> (def possible-keys [::all ::the ::keys])
user=> (eval `(s/def ::special-map (s/keys :opt-un [~@possible-keys])))
user=> (s/form ::special-map)
(clojure.spec.alpha/keys :opt-un [:user/all :user/the :user/keys])

Alex Miller (Clojure team)20:10:00

(note that even with :opt-un, you need qualified keys)

Alex Miller (Clojure team)20:10:35

in spec 2, there are several ways to do this and you can use it without any macros at all if needed

Alex Miller (Clojure team)20:10:29

in spec 2, one option is to resolve a symbolic spec and use register (no macros here):

Alex Miller (Clojure team)20:10:58

user=> (require '[clojure.spec-alpha2 :as s])
user=> (def possible-keys [::all ::the ::keys])
user=> (s/register ::special-map (s/resolve-spec `(s/keys :opt-un [~@possible-keys])))
user=> (s/form ::special-map)
(clojure.spec-alpha2/keys :opt-un [:user/all :user/the :user/keys])