unrepl 2017-08-21

Curious what people here think of: http://langserver.org/ vs the (un)REPL

for clojure(script) specifically

There isn't an eval :)

@dominicm but it is trivial to add an eval 😉

@rickmoynihan I had a proof of concept LSP implementation a while back. I’m all for it … just don’t have the time to write/maintain it currently

it’s a naive multiplexer to use when all you have is one pair of streams.

$ kein
Clojure 1.8.0
user=> (require '[unrepl.mux :refer [mux]])
nil
user=> (mux)
> [:a "(ns usera)\n"]
< [:a "user=> "] ; prompt upon connection
< [:a "nil"] ; value of the ns form
< [:a "\n"]
< [:a "usera=> "]
> [:b "(ns userb)\n"]
< [:b "user=> "]
< [:b "nil"]
< [:b "\n"]
< [:b "userb=> "]
> [:a "(+ 1 1)\n"]
< [:a "2"]
< [:a "\n"]
< [:a "usera=> "]
> [:b "(+ 2 2)\n"]
< [:b "4"]
< [:b "\n"]
< [:b userb=> ]
> [:a nil] ; close :a
> [:a "(+ 1 1)\n"] ; opens a new connection reusing the :a name
< [:a "user=> "]
< [:a "2"]
< [:a "\n"]
< [:a "user=>" ] ; not in usera 

it’s just the implementation I promised since I gave up on multiplexing input in unrepl proper: use a multiplexing layer below unrepl.

@cgrand, love these little experiments

so :a would be the virtual connection id right?

If I use a new conn-id, mux automatically opens a new session on the same stream?

yes, it allocates a new connection on the first message (for a given id)

it closes the connection on receiving nil.

the id can then be reused

ids are allocated by the client and it’s not a problem because they are scoped to the current “real” connection

and this doesn't require unrepl necessarily, correct?

I'm assuming so as your example uses a clojure.main/repl

and you can upgrade any session to what you want (including unrepl)

very nice modular approach

does it buffer output? I saw there's a pipe-size

this could be amazing for "inferior process" style tooling

$kein
Clojure 1.8.0
user=> (require '[unrepl.mux :refer [mux]])
nil
user=> (mux)
[:a "(mux)\n"]
[:a "user=> "]
[:a "[:a \"(inc 41)\n\"]\n"] ; evaluate (inc 41) in the :a session of the muxed :a session
[:a "["]
[:a ":a"]
[:a " "]
[:a "\""]
[:a "u"]
[:a "s"]
[:a "e"]
[:a "r"]
[:a "="]
[:a ">"]
[:a " "]
[:a "\""]
[:a "]"]
[:a "\n"]
[:a "["]
[:a ":a"]
[:a " "]
[:a "\""]
[:a "4"]
[:a "2"]
[:a "\""]
[:a "]"]
[:a "\n"]
[:a "["]
[:a ":a"]
[:a " "]
[:a "\""]
[:a "\\n"]
[:a "\""]
[:a "]"]
[:a "\n"]
[:a "["]
[:a ":a"]
[:a " "]
[:a "\""]
[:a "u"]
[:a "s"]
[:a "e"]
[:a "r"]
[:a "="]
[:a ">"]
[:a " "]
[:a "\""]
[:a "]"]
[:a "\n"]

output is not buffered (an amount of time-based buffering may be good (see above))

input is buffered so any input string (length of the read payload, not of the whole message) longer than 16k (default) may block the other sessions

(or a stalled session accumulating 16k of pending input)

backpressure could be implemented but I’m not sure it’s worth it

the important thing is the PoC

do you have a network programming background? you seem to know a lot about this

no, I’m just getting old 😉

so am I but not sure that magically gives me magic powers of protocol implementation 🙂

watching Alex Miller’s “Deps heaven” talk and realizing that with clj I can ditch my kein script

yes clj is cool, still in progress though, last time I checked I did not even see instructions in the repo anymore and scripts where only for Mac

Looking at it, I’m thinking that it needs a flag for easy repl server

I think you should have called it "klein" - "small" in German

that's actually a use case I've heard for unrepl - getting beginners started quickly

well I called it kein because kein andere JVM was started

fair enough

I made a small tweak to mux so that output is buffered at 20Hz: now mux on mux gives:

Clojure 1.8.0
user=> (require ‘[unrepl.mux :refer [mux]])
nil
user=> (mux)
[:a “(mux)\n”]
[:a “user=> “]
[:a “[:a \“(inc 41)\n\“]\n”]
[:a “[:a \“user=> \“]\n”]
[:a “[:a \“42\“]\n”]
[:a “[:a \“\\n\“]\n”]
[:a “[:a \“user=> \“]\n”]

I should port this to unrepl which suffer from the same unbuffered verbosity issue

It flushes after 50ms?

yeah, it flushes every 50ms. Do you see any issue with that?

it could lead to a noticeable lag when used interactively

would lowering it to 10ms (or 5ms?) have any drawbacks?

it flushes every 50ms if no applicative flush occured (and there’s one right after prompt).

the only purpose is to merge together outputs. Actually we get one message for each call to .write.

in unrepl this is an issue only when the user has some print calls: in one print call e.g. (prn {:a 1 :b 2}) then the output is

[:out “{” 5]
[:out “:a” 5]
[:out ” ” 5]
[:out “1” 5]
[:out “, ” 5]
[:out “:b” 5]
[:out ” ” 5]
[:out “2” 5]
[:out “}” 5]
[:out “\n” 5]
[:eval nil 5]

To summarize: when printing a lot to :out (`:err` should be kept unbuffered as usual) the output may be differed by up to 50ms.

makes sense

thanks for the explanation

I don’t think it will add any noticeable lag... unless you are doing a Zork speed run

upgrading unrepl to zork. Great idea

I count frames between character prints in order to measure the performance of my code. This completely breaks my workflow, could you please add an option to put it back without the flush please?

1

@dominicm for the most demanding users there's Mux Pro with 120Hz buttery-smooth printing. All messages are made from chars extracted from freshly mined Bitcoin blocks. Characters refills can be bought as in-app purchase.

💰 1

where can i preorder?