This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-04-08
Channels
- # announcements (1)
- # asami (4)
- # babashka (5)
- # biff (9)
- # calva (26)
- # cider (4)
- # clojure (23)
- # clojure-art (6)
- # clojure-europe (5)
- # clojure-japan (1)
- # clojure-norway (1)
- # clojurescript (11)
- # datomic (7)
- # emacs (26)
- # hoplon (3)
- # hyperfiddle (1)
- # inf-clojure (1)
- # introduce-yourself (5)
- # nyc (34)
- # podcasts-discuss (1)
- # releases (1)
- # shadow-cljs (9)
- # tools-deps (11)
I'm thinking how my ideal interaction with a socket REPL would look like.
What I don't want is having to have to have open code AND REPL side by side at all times (which is what one ends up doing with the socket REPL/inf-clojure combo as socket REPL doesn't give structured data but just undifferentiated output). My screen is 13" and I use a large font, so that's just unworkable. (I'm a lot on the go, second screen is a no-go at the moment, otherwise I'd just go for a bigger screen option and the side-by-side view would be fine).
Cider shows the results of expression, documentation etc in the source buffer (hence all I look at my code, which is exactly what I want). When an error happens, it opens an error buffer (in that case it's the thing I want to see at the moment, although in my case I'd rather drop into a sub-REPL, but anyway, it's where I'm happy to switch from code to a split view code/REPL).
I think this is the kind of workflow I'd like: but with a socket REPL (I like creating sub-REPLs which nREPL cannot do).
pPREPL is both: it's a socket REPL AND it works with semantic data.
Sounds like the best of both worlds, so why isn't it more supported? I can't see any pREPL-based Emacs plugin. inf-clojure will work with it of course, but in an unstructured way, that's not going to solve my limited screen space issue.
I get that Cider will stick to nREPL, fair enough, a lot of time invested there, but it's been a good while since pREPL came out and there's still no Emacs plugin for that?
What am I missing? Why is that not appealing to more people? Socket REPLs are popular, so why not add the little extra on top so the editor knows what's :tag :err
, :tag :out
vs :tag :ret
?
Better yet, it's even in Clojure by default, no need to install anything, no external dependencies, to me it sounds like the obvious thing to use. And it's got the best of both worlds, socket REPL with structured data.
TLDR; my question why pREPL isn't more supported, why it hasn't taken off? Am I missing some downsides? Why wasn't there the interest?
I'd even consider either forking inf-clojure and making it work on top of pPREPL Cider-style, if it's a reasonable thing to do, or starting a new plugin in order to get this...and trust me, I'm not particularly fond of Elisp, Clojure is really spoiling me 😇
particularly
> clojure.core.server/io-prepl
> pREPL is Clojure team’s answer to nREPL and critique that Clojure Socket REPL is not machine-friendly. It’s basically server/repl
but with EDN-formatted output:
> pREPL consumes raw Clojure forms but outputs EDN-structured data.
> In terms of what it does for you, it also formats exceptions (not in a Clojure-aware way, unfortunately) and synchronizes your output so that two threads can’t print simultaneously. But that’s about it.
> The main problem with pREPL is that it’s based on EDN and, thus, aimed at Clojure clients first and foremost.
the nice thing about nREPL is that it uses bencode, which is a fairly easy thing to write a parser for in whatever language (elisp) you're in. writing an EDN parser is quite a bit harder and might introduce further issues
I think pREPL is super cool tech but "build it and they will come" doesn't always work in tech. I think the people who are willing to put in the work don't see the tradeoffs worth it to consider something else atm. But that could change! Maybe you will be the one to build it 😄
Very cool blog post, thanks for sharing!
the nice thing about nREPL is that it uses bencode, which is a fairly easy thing to write a parser for in whatever language (elisp) you're in. writing an EDN parser is quite a bit harder and might introduce further issues Yep. Elisp isn't the best language for such task.
yeah. there is parseedn
but idk how fast it is or what other issues may arise by using it. bencode and strings are well understood at this point
I think it could start just plain socket REPL and then upgrade it to whatever else and in case of Emacs dev it could just wrap pREPL result in (to-json)
and there's surely some JSON parser in Elisp.
@U4YGF4NGM good to know.
Oh yeah that actually looks good. Good chance it'd do the job.
I think I'll model how exactly my workflow would look like and try doing it with inf-clojure for now. I've done some experiments yesterday and I have a fair idea, but I want to see exactly how much benefit will I get from socket REPL (sub-REPLs are a big deal for me so yeah). If I'm happy with it, I will look to make a plugin (or fork inf-clojure or potentially copy some code from Cider, who knows), because the workflow is really important for me – even if I have to work with Elisp, I want this to work. Particularly since all of it has been done before in Cider and inf-clojure, even without much of Elisp experience I think it's doable (albeit very time consuming).
I once found https://github.com/saikyun/preppy if you want a very early starting point to look at. I've thought about doing something like this too but never started on it
@U064UGEUQ thanks, I'll have a look. You're more than happy to join! I could appreciate some help. Also @U4YGF4NGM or whoever else might be interested!
At this stage I'm 100% decided I'm doing it and I'm doing exploratory works, so far just in terms of the desired workflow:
• I have (doseq [i [1 0]] (break) (/ 1 i))
working (`:repl/quit` or C-d
quits the breakpoint and make it proceed to the next expression.
• Starting subREPLs automatically on error is fun when typing into the REPL, but useless for Emacs, as any error would mean having to switch to the REPL buffer and send :repl/quit
. That'd get old pretty bloody fast.
• So instead one would set manually when they want to drop into a subREPL using something akin to (doseq [i [1 0]] (try (/ 1 i) (catch Exception e (break))))
. This works, but it's too much bloody typing, so there'd be another macro to wrap that into something say (doseq [i [1 0]] (try-break (/ 1 i)
, probably aliased to something even shorter as that's a very common thing to use. You do have access to the value of i
there obviously, that's the whole point of this little exercise!
Obviously this would just come as an optional utils library, anyone's free to use whatever they please!
Now I think this is a good starting point for me to actually start diving into the Elisp part (next weekend?). I know feck all about Elisp (whopsie!), which is why I'd really appreciate someone joining me in this endeavour, but one way or the other, it's a tool I want and need and it's gotta happen sí o sí.
I'm also working full-time, so the timeline is in the air, but either way it's high priority thing for me (also subject to how badly do I need it in my work,).
The initial version will concentrate on one single thing: making C-x C-e
(eval last sexp) work with displaying result in the source buffer temporarily, just like Cider does with a caveat:
• STDOUT/STDERR will only get shown in the REPL buffer itself.
• Return value will get shown in the source buffer Cider-style.
• Any error will also get shown in the source error (something like #error {:message (ex-message *e)}
possibly also with :data (ex-data *e)
, although that might require some truncating, whatever.
That way I know if the code I'm working on works and if not, the short message summary is usually enough to know what the problem is, if not, go play in the REPL buffer to find out.
Unlike in Cider this workflow would require some typing into the REPL. Right, that's why we start the bloody subREPLs in the first place, so we can go and inspect what's going on there.
Say you have (doseq [i [1 0]] (try-break (/ 1 i)))
. Run C-x C-e
on it. Then you have to switch to the REPL and do whatever you gotta do! Inspecting the value of i
typically. When done, :repl/quit
or C-d
.
So this is my idea so far. It's a workflow worth pursuing, even if it means learning some bloody Elisp 😎
Feedback to the workflow also appreciated. How would you like to use it?
(defmacro try-break [expr]
`(try ~expr (catch Exception ~'*e (break))))
Suggestions welcome what simple short name could be used for this.Yeah I'd be interested in helping out, at the very least trying out what you come up with and hacking on it if there's stuff i'd like to see. And your workflow sounds good for an MVP. I'm not great at elisp either but have been increasingly trying to use it more for a few months.
@U064UGEUQ OK that'd be really appreciated. It's a lot of stuff for one person (not to mention I'm not yet too experienced in REPL-driven development in Clojure, so I need someone to make me aware of various pitfalls before I loose tons of time going down the wrong path). I'm going to polish the documentation for what I have and publish it as a proof-of-concept repo with all the rubbish quality code in it and we can take that as a starting point for the discussion about specifics.
Though https://github.com/borkdude/clj2el and/or https://github.com/jackrusher/scimacs seem to have potential for reducing the need to learn elisp to an extent, I wonder how much those could be leaned for this type of thing (I haven't got hands on with either yet but will try to this week)
Hahaha I wonder if there's something Michiel haven't wrote yet 😄
Cool, I'll check 'em out!@
@U064UGEUQ let's create a channel for it and move a discussion there, so we can refer to older posts.
emacs-prepl
sounds like a reasonable name?