This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-08-03
Channels
- # announcements (12)
- # babashka (36)
- # beginners (126)
- # calva (26)
- # cider (10)
- # clj-kondo (71)
- # cljdoc (3)
- # cljsrn (2)
- # clojure (232)
- # clojure-australia (1)
- # clojure-europe (11)
- # clojure-france (20)
- # clojure-nl (4)
- # clojure-norway (1)
- # clojure-serbia (4)
- # clojure-uk (6)
- # clojurescript (62)
- # conjure (5)
- # cursive (12)
- # data-science (1)
- # datomic (57)
- # deps-new (1)
- # duct (3)
- # emacs (5)
- # events (8)
- # fulcro (6)
- # graalvm (5)
- # helix (3)
- # jobs (6)
- # jobs-discuss (3)
- # kaocha (4)
- # lsp (128)
- # malli (12)
- # missionary (22)
- # off-topic (1)
- # pathom (7)
- # polylith (27)
- # quil (1)
- # re-frame (20)
- # react (9)
- # reitit (12)
- # releases (8)
- # remote-jobs (3)
- # sci (3)
- # shadow-cljs (9)
- # spacemacs (10)
- # tools-deps (7)
- # vim (7)
- # xtdb (14)
What's the difference between nREPL and socket REPL? Well, I can imagine the socket REPL, I'm more confused about nREPL. I see nREPL is being used more than socket REPL, correct?
here's a great article about almost all the Clojure REPLs: https://lambdaisland.com/guides/clojure-repls/
here is another great article by the author of #conjure that talks about yet another REPL, prepl, that's not covered in the article above: https://oli.me.uk/exploring-repl-tooling-with-prepl/
most likely. a socket repl is largely exactly what you see on your terminal, except that the input and output are sent over a socket. If you start up a socket repl, then you can use netcat to connect to the socket, and it will feel largely just like a normal terminal repl. nREPL is very different. It also listens on a socket, but rather than just normal input, it expects structured messages. For a socket repl, you would just send (+ 1 2 3)
and the process would put back on the socket 6
and then a prompt. for nREPL, you would put the message {:op "eval" :code "(+ 1 2 3)"}
and it would return a response map {:status :done :value 6}
So why use one over the other? Socket repls are built into the clojure language and don't require a special main. What that means is that just setting some system properties will have this socket repl started, regardless of anything else going on. So no special entry to start up the socket server. Big win. It is also "just a repl": it is not overly complex, and it just a thin entrypoint over a normal repl. The downsides are that there is no tooling channel. Every time you send something over it, you are advancing the most recent result vars *1
, etc. So if tooling asks for a docstring, or completions, it has advanced *1
and you may not notice. It also is "in a namespace" so if you are interested in code in another namespace you need to use fully qualified names or change the namespace of the repl.
On the other hand, nrepl can more easily open tooling channels. Further, it can have other :op
(operations) rather than just eval
as listed above. You can make middleware that does anything with the runtime, from completions, finding usages, etc. The downsides are that there is quite a bit of complexity involved, from dynamic class loaders, more threads. This can actually affect your repl and there can be subtle bugs. Further, the standard way of starting nrepl is using a main from the library that starts up an nrepl server, which means you have to start the repl with that main, not whatever you might normally do.
I have strong feelings about this 🙂
prepl is a illustrative case, it leaves input stream processing alone and changes how output is delivered
the last time I got in a big discussion about this with alex, the insistence was that a clojure.main/repl is different because it is defined on streams, not on rpc style messages
I think that is pretty meh as an explanation, because a stream is an ordered sequence of messages, and having written a few bits of code hooking both clojure.main/repl and nrepl to different transports I have not found a huge difference at that level
which is how I have come to the conclusion that the difference is entirely around how input is handled
if nrepl switched from having an "eval" op to just using the "stdin" op they would basically be the same
practically speaking the difference can come up if the nrepl client disagrees with clojure.core about how to read input / what's a full or well formed input, I've seen a few of these bugs over the years
yeah, and the input handling difference is not the only difference, but I think it is the root, all the other differences can be traced back to that
socket repl is something people get focused, but really we are talking about a clojure.main/repl with its inputs and outputs attached to a socket
the difference between nrepl and a clojure,main/repl is basically how input is treated
nrepl differentiates between inputted code to evaluate and user input to the program being run
OK, thank you all 🙏:skin-tone-3:
So for tooling like Cider, Calva and the likes, nREPL is more practical I assume?
Yeah that's why I ask also
I use Socket REPL (only) which is why I use Clover for VS Code (and previously used Chlorine for Atom). I like that because then I can use the exact same editor-based workflow with "any" Clojure process, since I can ask Clojure itself to start a Socket REPL at process startup, just by providing JVM options.
(both Clover and Chlorine use unrepl which is a project that can "side-load" some code over a Socket REPL to then start a second "control REPL" and that provides some nREPL-like functionality -- the nREPL team have been talking about side-loading for a while, which would expand my available toolset)
@seancorfield OK, that's interesting. So the main difference (as far as your personal workflow is concerned) is that CLJ starts the REPL itself?
on a somewhat related note, are there any longer-form examples of using prepl
for the type of "program to program communication" that Rich described in his https://nextjournal.com/mk/rich-hickey-on-repls about REPLs? The only stuff I've seen is very basic "here's how you open a prepl
and send forms to it," which I don't think covers the whole thing.
> I think an edn-based REPL makes a fine RPC server for program-to-program communication, especially since the set of 'verbs' supported is bounded only by what you can evaluate, and the types of things you might discover is open.
I guess I can take that as about as clear a statement of the "party line" @hiredman described (def worth a read in the context of this discussion).
It seems like nREPL
, having come first, handles many of the use cases people would use prepl
for (var lookups, editor tooling, etc). But the programming problem he gives at the end of the post is a really simple and intriguing example of the idea of building an application out of a REPL (e.g. leveraging the power a running REPL gives you in handling user input), something that I haven't seen that much before. Are there other examples of applications being built on top of REPLs (as opposed to the more typical "built with the help of a REPL" in REPL-driven dev)?
the coolest feature nrepl doesn't have because of the way it differentiates the inputstream is nested repls. in a clojure.main/repl you can call clojure.main/repl to get a nested repl, or call other functions to connect your repls inputs and outputs to a remote repl, and that can be repeated ad nauseam
yeah. i made one that puts each form and its subforms into a graph database that can be searched from the repl
wow, this is amazing, thank you!
@hiredman OK, nested REPLs, that sounds pretty useful
So only socket REPLs can be nested then?
again, people keep saying "socket repl", but I think a better name for it is a clojure.main/repl
Good point.
cool thread on http://ask.clojure.org here. please add cool additions as you come across them:
the "socket repl" people are talking about is a clojure.main/repl attached to a socket
for nrepl I've written some custom middleware to try and do that sort of thing, and it sort of worked, but you have to write the middleware and it gets really funky, and that is only with one level of nesting
it doesn't rely on socket repls. as hiredman said, it is just that clojure.main/repls are nestable. Using a clojure.main/repl with the socket repl functionality is just the easiest way to connect tooling to one
looks like it uses clojure.core.server
aka prepl
, right?
from my deps.edn
:socket {:jvm-opts ["-Dclojure.server.repl={:port,50505,:accept,clojure.core.server/repl}"]}
Ah, I assumed core.server.prepl
and core.server.repl
were not separate things. My mistake!
Loved this for Stuart's talk about REPL driven development. (RE: subrepls)
similar to stu's break thing https://gist.github.com/hiredman/a2630ea6153d06840a2723d5b2c9698c sends a clojure.main/repl over tap>
Hi! Are there any strategies about how to make the mindset switch from a non FP style of programming to FP? The first think that comes to mind when I think about writing a function is code similar to: int someFunction() {int result = 0 (...magic..) return result}
wrong button 🙂
I wanted to send it. I just did not want the entire message to be enclosed in a code block
I don’t have general advice, but for me it was reading through SICP. I had several “AHA” moments when going through it. I’m actually re-reading it currently so get used to reading scheme a bit before reading Sussman’s new book.
I actually started reading SICP but it did not really work for me mainly because of scheme. Thus I wanted to do some clojure before revisiting the book :)
I did look into several books including haskell books. I have the feeling this is analogous to thinking about the domain model first vs how to save the data into the DB
@mail802 I think Eric Normand has some good ideas on how to learn the FP mindset; may be a good idea to check out his book [0] and/or podcast [1] [0] https://www.manning.com/books/grokking-simplicity [1] https://lispcast.com/category/podcast/
+1 @pithyless One book that helped me think in the FP way (“data transformation” as opposed to “step by step instructions”) is Grokking simplicity.
thank you all for your suggestions!
... and with that kind of pitch @deleted-user I shall add it towards the top of my own reading-list. :)
I usually read multiple books on the same topic and at some point in the process it clicks 😁
That’s a reading technique I read about in an article just, yesterday? I do that too, didn’t know it was a thing.
yes, very well done. I went through some for another language. And it’s just nicely put together.
I also have exercism on the radar. I also started with it and stopped. I wanted to do something like deliberate practice before tackling something like SICP or exercism :)
I mean it is deliberate practice right? Or do you mean in the sense of going through the same mechanical motions repeatedly?
Right now I use almost the same mechanics as I use in my day job language. But I feel like I’m missing out!
By deliberate practice I mean nut just solving the exercise but also understanding why it has been solved in a specific way and training my brain to do pattern matching 😄. Knowing how to approach something when I see it again.
Concerning pattern matching - that is not native to clojure. There is a library providing pattern matching though.
Oh yeah that’s definitely what works for me too. Just doing the exercise without further thinking doesn’t typically internalise the concept deeply. Another thing that helped me in the past is looking at how other people write code, hopefully with commentary.
I do a ton of live coding too during a course because of this. I think it really helps because you can see subtle things that cannot easily be explained.
The students then see how my cursor moves and what exactly I mean when I say something. Pretty sure there are some good videos of Clojure programmers explaining things while they write. I think its very valuable.
For my own personal projects (so free to choose anything) is there any reason I shouldn't use java 16 over 11? I guess it's not a LTS (which will happen with the next release, java 17?) but is Clojure fully compatible with the latest jvm stuff?
@chase-lambert We use 11 in production but we've been testing everything against 14, 15, and 16, so that we can move to 17 once it is available (well, probably after the first couple of patches have been released 🙂 ).
If you have any code that relies on reflective access (per Java modules), my understanding is that will break in 17. In previous releases, you could specify --illegal-access=warn
as a JVM option to catch all occurrences (rather than it warning about just the first occurrence) and in 16 that gets you a warning that
OpenJDK 64-Bit Server VM warning: Option --illegal-access is deprecated and will be removed in a future release.
and I believe that option is being removed in 17 and such "illegal access" will be prohibited.sorry to ask, but how is Clojure affected by this change? Can we expect any kind of illegal access exceptions more often?
Clojure itself isn't @UBLU3FQRZ but you may well run across it in Java libraries that Clojure code uses. clojure.xml
trips over this, using SAX stuff, as I recall, but it's a deprecated namespace anyway that no one should be using now.
I think it's good to test your Clojure apps against 16 but given the short "lifespan" of the non-LTS versions, even the various openjdk supporting companies seem to only focus on the longer-lived LTS versions (8, 11, 17).
unless you have some known illegal accesses lying around or its a particular devops issue to do so, its generally best to stay up to date
LTS only matters if you are 1. Using a platform providing the JDK and they only provide certain versions 2. You are paying for paid support from a vendor
at work we are still on 11, but thats just because we haven't had a reason to prioritize it over the other work we need to get done
I currently use 16 for everything Clojure, which are either just personal projects or data migration / automation scripts that I use in my dayjob. I also get deprecation warnings but no real issues.
Hey @hiredman, @seancorfield mentions in his REPL Driven Development, Clojure's Superpower video that you use Emacs with clojure.main/repl REPL. So no cider then. Would you mind giving us an idea of your workflow with REPL in Emacs?
M-x shell, then clojure, clj, or something else that opens a repl, then I kill and yank (copy and paste) forms into the repl as needed
@hiredman OK. So you don't use anything like "eval last sexp" and the likes?
Interesting. Why? I mean evaling last sexp is something I do all the time.
OK, fair enough.
I have no idea how fast the churn is, since I've been on CLJ for 1 month only, so ... :)
I dunno, plenty of people are fine with it, but it just drives me nuts when I have a working setup, move to a new machine and suddenly things are broken because el-get grabbed a newer version or whatever
Yeah I get that. Keeping Emacs configuration stable is non-trivial when there are many packages, no doubt about that.
i use cask
and can pin versions:
(depends-on "cider" "1.0.0")
(depends-on "clj-refactor" "2.5.0")
i haven’t tested this. in the past i have gone as far as having a stable emacs/cider/nrepl el tree in a private git repo.
I use guix to manage my Emacs deps, but it doesn’t really have a good clojure story yet.
@U1Z392WMQ oh that's nice. I use leaf, which I really like (use-package was a bloody pain), but I don't think it does versioning.
This was a big part of why I finally gave up with Emacs. I'd been using it on and off for almost twenty years and I just got tired of the fragility and churn. I've toyed with the idea of going back to it with just a minimal config using inf-clojure
and a Socket REPL... but I've gotten very happy with VS Code and I love that I can write my own extensions etc in ClojureScript (executed via sci
under the hood in Chlorine/Clover I believe).
this is almost exactly my setup except that i use inf-clojure
which is essentially a shell but has nicer ways to eval last expression and such
would you happen to know why inf-clojure
keeps repeating the prompt in the repl buffer?
Great! Would love to hear your reasons for using inf-clojure over Cider if you may please.
I can see inf-clojure
is lightweight, but it has the basic features, such as eval last sexp and the likes.
So I imagine one of the advantages would be one can easily extend the REPL, since it's clojure.main/repl
, so one can start sub-REPLs (as discussed in the channel a bit earlier), do the break
thing as I posted from Stu's talk and so on.
Any other differences worth noting? Both positive and negative.
@U01KZDMJ411 i bet you are just starting the process inside inf-clojure? I always start a socket repl and connect to that. But i'm not sure