Fork me on GitHub
Jakub Šťastný15:08:49

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:

👍 3

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:

👍 3

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.


nREPL is not a real REPL, it's an RPC framework


this is the core party line

📜 3

I have strong feelings about this 🙂


is prepl considered an rpc framework as well?


prepl is a illustrative case, it leaves input stream processing alone and changes how output is delivered


so the big difference is in how input is handled


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


clojure.main/repl does not

Jakub Šťastný17:08:06

OK, thank you all 🙏:skin-tone-3:

Jakub Šťastný17:08:28

So for tooling like Cider, Calva and the likes, nREPL is more practical I assume?


CIDER uses nrepl exclusively and has no socket repl support

Jakub Šťastný17:08:12

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)

Jakub Šťastný17:08:19

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

☝️ 3

yeah. i made one that puts each form and its subforms into a graph database that can be searched from the repl

😮 3

wow, this is amazing, thank you!

Jakub Šťastný17:08:12

@hiredman OK, nested REPLs, that sounds pretty useful

Jakub Šťastný17:08:32

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


cool thread on 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

☝️ 3

so clojure.main/repl can be nested


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?


those aren't linked i don't believe


from my deps.edn :socket {:jvm-opts ["-Dclojure.server.repl={:port,50505,:accept,clojure.core.server/repl}"]}


so certainly not prepl here


Ah, I assumed core.server.prepl and core.server.repl were not separate things. My mistake!

Jakub Šťastný17:08:44

Loved this for Stuart's talk about REPL driven development. (RE: subrepls)


the (grepl/grepl) in the readme example is launching a nested repl


similar to stu's break thing sends a clojure.main/repl over tap>

Gabriel Kovacs17:08:49

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} 

Gabriel Kovacs17:08:42

wrong button 🙂


wrong button as in you didn’t mean to send the message?

Gabriel Kovacs17:08:00

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.


Or maybe any beginner Clojure material that talks about recursion

Gabriel Kovacs17:08:07

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


Thinking deeply how code evaluates, how it is through and through expressions.

Gabriel Kovacs18:08:32

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] [1]

❤️ 3

+1 @pithyless One book that helped me think in the FP way (“data transformation” as opposed to “step by step instructions”) is Grokking simplicity.

Gabriel Kovacs18:08:04

thank you all for your suggestions!


also I think what you said above Gabriel is kind of an important insight


... and with that kind of pitch @deleted-user I shall add it towards the top of my own reading-list. :)

bubblebobble 3
Gabriel Kovacs18:08:41

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.


“Syntopic” reading

Gabriel Kovacs18:08:09

@deleted-user I appreciate the suggestion, I am not dismissing it 😄

👍 3

I also think going through exercises regularly for a while helps.


Doing is understanding


yes, very well done. I went through some for another language. And it’s just nicely put together.

Gabriel Kovacs18:08:34

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?


I was thinking of doing that for editor mechanics


Right now I use almost the same mechanics as I use in my day job language. But I feel like I’m missing out!

Gabriel Kovacs18:08:58

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.

👍 3

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.

👍 3

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


I would always use the newest JDK


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


Sounds good folks. Looks like 17 is coming up here in September too


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.

Jakub Šťastný21:08:50

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

Jakub Šťastný21:08:51

@hiredman OK. So you don't use anything like "eval last sexp" and the likes?


I use clojure-mode and paredit, but not really anything that ties that to the repl

Jakub Šťastný21:08:59

Interesting. Why? I mean evaling last sexp is something I do all the time.


I am not interested in trying to keep up with the churn around cider/nrepl

Jakub Šťastný21:08:18

OK, fair enough.

Jakub Šťastný21:08:38

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

Jakub Šťastný21:08:15

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.

Jelle Licht22:08:33

I use guix to manage my Emacs deps, but it doesn’t really have a good clojure story yet.

Jakub Šťastný22:08:09

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

Jakub Šťastný22:08:25

Great! Would love to hear your reasons for using inf-clojure over Cider if you may please.

Jakub Šťastný22:08:58

I can see inf-clojure is lightweight, but it has the basic features, such as eval last sexp and the likes.

Jakub Šťastný22:08:05

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.

Jakub Šťastný22:08:29

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


there are some issues relating to that


negatives: there is no tooling, so navigation, completion dont work


but i supplement with clojure-lsp for that stuff


I also start a socket repl from the shell and connect to that but ok thanks, might look into it someday


but if you don't get that behaviour I should probably check my config then


is it during long running evaluations?


no, just switching between the repl buffer and the code buffer makes it happen


not always, but enough times that is very easy to reproduce