Fork me on GitHub
#beginners
<
2019-10-30
>
Noah Bogart01:10:07

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

seancorfield01:10:08

@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

seancorfield01:10:17

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!

dpsutton02:10:22

@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?

dpsutton02:10:27

or just kinda scouts honor?

seancorfield02:10:30

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

dpsutton02:10:04

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

seancorfield02:10:44

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

seancorfield02:10:13

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

seancorfield02:10:06

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

seancorfield02:10:45

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?

didibus02:10:38

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

didibus02:10:56

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

seancorfield03:10:54

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

seancorfield03:10:07

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

seancorfield03:10:53

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

seancorfield03:10:57

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 🙂

didibus03:10:07

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.

seancorfield03:10:45

What "debugging" do you rely on?

didibus03:10:57

Cider debug

didibus03:10:06

It's pretty amazing

seancorfield03:10:12

I have no idea what that does.

didibus03:10:47

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

seancorfield03:10:16

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

didibus03:10:17

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

didibus03:10:32

Well, in other languages it's pretty annoying

didibus03:10:55

But in Cider its integrated as part of the repl

seancorfield03:10:18

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

seancorfield03:10:50

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.

seancorfield03:10:25

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

didibus03:10:27

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
didibus03:10:37

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

didibus03:10:03

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

didibus03:10:08

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

seancorfield03:10:37

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
didibus03:10:28

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

bfabry03:10:30

huh, til tap. seems nice. seems like it would mesh very well with something like http://honeycomb.io

didibus03:10:06

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

seancorfield03:10:02

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.

seancorfield03:10:23

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

didibus03:10:33

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

didibus03:10:13

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

didibus03:10:24

So rebel-readline is an amazing alternative

seancorfield03:10:10

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 🙂

bfabry03:10:31

“it runs on java” cures many ills 😆

👍 8
seancorfield03:10:14

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

seancorfield03:10:20

(it was Neal Ford, right?)

mg03:10:27

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

seancorfield03:10:55

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 🙂

seancorfield03:10:27

(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
jumar09:10:57

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 com.google.guava/guava, 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...

jaihindhreddy11:10:41

^ 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) https://www.reddit.com/r/Clojure/comments/dp1p2t/any_mature_open_source_system_to_learn_about/

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?

jumar13:10:54

@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? https://github.com/circleci/frontend

jumar18:10:37

In a recent REPL podcast they mentioned they are moving to JavaScript/React: https://www.therepl.net/episodes/29/ 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!

bbloom16:10:57

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

indy16:10:19

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

hiredman16:10:43

What kind of output?

indy16:10:43

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

hiredman16:10:55

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

hiredman16:10:25

There is nothing about swap! that prints things

indy16:10:03

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

bfabry16:10:22

this is just swap! returning the value

hiredman16:10:25

You are using a repl

hiredman16:10:33

Read eval print loop

hiredman16:10:41

It prints the result

indy16:10:01

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

andy.fingerhut16:10:14

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

indy16:10:00

I tried do with the last exp as nil

hiredman16:10:00

(do whatever nil)

indy16:10:13

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

andy.fingerhut16:10:29

What does your code in -main look like?

hiredman16:10:20

You may have some annoying lein plugin breaking things

andy.fingerhut16:10:14

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

indy16:10:02

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

andy.fingerhut16:10:19

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.

indy16:10:22

Yeah tried writing my own lisp interpreter as learning project

bfabry16:10:41

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

indy16:10:16

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

bfabry16:10:27

oh but (= expr) is even shorter!

noisesmith17:10:09

I might be misinterpreting but

user=> (= nil)
true

bfabry17:10:33

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

noisesmith17:10:55

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

indy17:10:12

is shorter isn't it?

andy.fingerhut17:10:12

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

donyorm18:10:14

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

bfabry18:10:01

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

donyorm18:10:20

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

didibus18:10:42

You need a multi-spec

didibus18:10:34

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

bfabry18:10:53

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

donyorm18:10:13

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

donyorm18:10:29

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

didibus18:10:27

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

didibus18:10:46

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

didibus18:10:02

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

didibus18:10:29

Specs are data, but parsing them is a bit annoying

didibus18:10:37

But it's easy, just a bit ugly

didibus18:10:57

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

didibus18:10:28

And from that you can grab the req vector back

didibus18:10:49

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

didibus18:10:45

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

donyorm18:10:13

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

didibus18:10:38

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

didibus18:10:54

It gets replaced before macroexpansion

didibus18:10:18

But I might be wrong

didibus18:10:22

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

donyorm18:10:40

Ok, thanks for the suggestions!

didibus18:10:56

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

didibus19:10:14

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

didibus02:10:48

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

didibus02:10:49

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

didibus07:10:04

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/possible-keys
user=> (eval `(s/def ::special-map (s/keys :opt-un [~@possible-keys])))
:user/special-map
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])
nil
user=> (def possible-keys [::all ::the ::keys])
#'user/possible-keys
user=> (s/register ::special-map (s/resolve-spec `(s/keys :opt-un [~@possible-keys])))
:user/special-map
user=> (s/form ::special-map)
(clojure.spec-alpha2/keys :opt-un [:user/all :user/the :user/keys])