grmbl a “real” repl in (bootstrap) cljs needs a lot to be sorted out.
• input abstraction • bindings handling
bindings, var-set
support would be the MVP (minimum viable patch ;-))
@cgrand I don't think the normal REPL model would be all that useful in JS
as most operations will be async
and the normal model really relies on sync
and blocking
most IO are async, true
what model do you propose?
I have no idea
I treat it as RPC at the moment
just faking a REPL on top of it in the CLJ side
the JS side is pure RPC
I’m focusing on selfhosted cljs atm so there’s no CLJ side
well call it compiler
and eval
sides
and the JS side is not pure RPC because of the async nature: when the RPC returns processing may still be going on.
yes but you get a Promise or something usually
(I don't handle *out*
)
this is the JS side of the node REPL .. it is connected to a websocket that gets messages from the server (CLJ)
it takes the msg, calls (js/eval ...)
and sends the result back
guess thats not RPC since the server doesn't call it
but each :repl/invoke
msg produces one :repl/result
msg
but the client can decide how to handle that message, ie. the browser impl is different
Ok but this has nothing to do with the hurdles that I have.
tried to use it as an example of how I think any REPL in JS will look really
To summarize: there’s no generic way to write a repl in selfhosted CLJS because:
1/ there’s no agreed upon *in*
(or equivalent), so no R
2/ there’s no agreed upon eval
(planck has one), so no E
bonus/ bindings conveyance requires hack unless you want dynvars (print-fn ns etc.) to leak from one repl to the other
1) browsers do not have an *in*
at all
(for the last point, start lumo
in socket server, open two repls, changing ns in one, change the ns in the other)
2) there is JS eval
so you need to define C
for compile
RCEPL
no: E is compile+js/eval
RCEP_L_ bold happens on the "server" side
well that didn't work
RC+L
CLJ EP
JS
1) I’m not considering browsers
you are not considering non-selfhost
not at the moment, one step after the other
well my suggestion would be to start with normal CLJS not self-hosted CLJS
anything that works with normal can work self-hosted, not true the other way around
I disagree on “anything that works with normal can work self-hosted” because most of a normal CLJS repl is CLJ and uses blocking IO
well you can write the CLJ side async, so yes "anything" is incorrect
but you have to account for the 2 runtimes issue
selfhosted is easier than normal because there’s not two envs to deal with, it’s closer to CLJ (minus sync IO) but needs some gaps to be filled.
selfhosted is also very limited and not very useful for big apps (and not an option for browser targets)
I wouldn’t bet against it in the long run
hmm dunno ... not too optimistic for node.js
as a runtime. It is horrible.
so I really wouldn't write anything server side in pure CLJS not because of self-hosted but because of JS
9 years ago I started working with Clojure because I got badly burnt doing server-side JS 😄
and you want to go back? 🙂
Vendetta 🙂
no threads still so not much has changed
to make things worse it had threads (Rhino)
(threads + JS = 💣 )
yeah dunno, I don't get how you could write a serious server application in a runtime that was strictly built and optimized for the browser
heck I spent soo much time even trying to get my browser apps performant enough to be acceptable and that is just one user at a time.
@cgrand, well node.js has streams, which do resemble *in*
and *out*
but bindings are no good if they don't survive across events
yeah but they are specific to node
*out*
and *err*
are already covered by *print[-err]-fn*
well, only if you can print synchronously, right?
but yeah typically you can print synchronously, even in browsers
that’s why in evented-reader I introduce a Stream abstraction to create *in*
. Then the author of the root repl may provide an impl
Ok the bridge is working.
(nrepl-bridge/repl & nrepl-args)
recreates a pair of stdin/stdout to a clojure.main/repl on top of nrepl
; the following example upgrades the running repl to mirror the replized nrepl :-)
(mirror (repl :port 62005))
So now we can work either with a socket repl or a nrepl (as long as the client is written in Java…)
Perverse idea: upgrade a repl to nrepl…
if you supported bencode, all would be fine
is there an nRepl
protocol spec somewhere?
the readme is it pretty much
The spec isn't bit
*big
the spec defines that messages going in must be a map with an op
key.
when an op has been handled, a message must be sent over that transport with a :status
including "done"
in the result
the bencode transport is just a transport mechanism for those maps.
Common ops are included in tools.nrepl
if a message has an :id
it should come back with an :id
(it's possible that this is done by merging into the incoming map, but I'm not certain)
uhm, seems quite easy to replicate indeed
Yeah but then there's the middlewares
@cgrand The middleware is really just an implementation detail of tools.nrepl. I'm "out there" and I have 4 middlewares in my profile.
I suppose 5 if you count piggieback for cljs.
Middlewares are based on dependency ordering using a topological sort. It's not super easy, I'll admit. But it isn't crazy (in fact, it's already done!)
https://github.com/stuartsierra/dependency makes it easier
Ok but don't count me in for the implementation.
heh.
@cgrand you're extremely productive. I wanted to ask how you approach problems & get stuff done.
The secret is having some accounting or paperwork to do :-)
Seriously I don't consider myself as extremely productive. I'm productive by bursts when ideas have been on the back burner (or hammock) long enough
I agree with that, I tend to ruminate on a problem for ages and spend a long time investigating it, and then have short bursts of activity when I actually do the work.
And accounting and paperwork does indeed also help 🙂