unrepl

discussing specification of an edn-based repl and its implementations.
2017-08-21T08:46:17.000343Z

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

2017-08-21T08:46:28.000280Z

for clojure(script) specifically

dominicm 2017-08-21T08:53:40.000316Z

There isn't an eval :)

thheller 2017-08-21T09:06:05.000054Z

@dominicm but it is trivial to add an eval 😉

thheller 2017-08-21T09:06:56.000021Z

@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

cgrand 2017-08-21T09:37:56.000085Z

Small hack over the weekend: https://github.com/Unrepl/mux

cgrand 2017-08-21T09:40:39.000050Z

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

cgrand 2017-08-21T10:00:16.000130Z

$ 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 

2017-08-21T10:35:02.000231Z

interesting…

cgrand 2017-08-21T11:06:05.000023Z

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

pesterhazy 2017-08-21T12:53:12.000003Z

@cgrand, love these little experiments

pesterhazy 2017-08-21T12:54:48.000139Z

so :a would be the virtual connection id right?

pesterhazy 2017-08-21T12:55:40.000322Z

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

cgrand 2017-08-21T12:57:18.000153Z

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

cgrand 2017-08-21T12:57:45.000314Z

it closes the connection on receiving nil.

cgrand 2017-08-21T12:57:53.000218Z

the id can then be reused

cgrand 2017-08-21T12:59:28.000280Z

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

pesterhazy 2017-08-21T12:59:42.000047Z

sweet

pesterhazy 2017-08-21T12:59:53.000330Z

and this doesn't require unrepl necessarily, correct?

pesterhazy 2017-08-21T13:01:33.000167Z

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

cgrand 2017-08-21T13:01:35.000151Z

correct

cgrand 2017-08-21T13:02:00.000031Z

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

pesterhazy 2017-08-21T13:02:12.000321Z

very nice modular approach

cgrand 2017-08-21T13:02:18.000299Z

mux on mux:

pesterhazy 2017-08-21T13:02:25.000222Z

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

pesterhazy 2017-08-21T13:03:05.000038Z

this could be amazing for "inferior process" style tooling

cgrand 2017-08-21T13:03:17.000286Z

$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"]

cgrand 2017-08-21T13:04:16.000281Z

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

cgrand 2017-08-21T13:05:24.000105Z

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

cgrand 2017-08-21T13:06:00.000246Z

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

cgrand 2017-08-21T13:06:34.000205Z

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

pesterhazy 2017-08-21T13:07:10.000306Z

the important thing is the PoC

pesterhazy 2017-08-21T13:07:58.000254Z

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

cgrand 2017-08-21T13:08:12.000078Z

no, I’m just getting old 😉

pesterhazy 2017-08-21T13:10:09.000283Z

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

cgrand 2017-08-21T14:36:42.000242Z

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

richiardiandrea 2017-08-21T14:40:09.000287Z

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

cgrand 2017-08-21T14:43:27.000582Z

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

pesterhazy 2017-08-21T15:01:27.000763Z

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

pesterhazy 2017-08-21T15:01:54.000297Z

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

cgrand 2017-08-21T15:07:45.000366Z

well I called it kein because kein andere JVM was started

pesterhazy 2017-08-21T16:09:13.000061Z

fair enough

cgrand 2017-08-21T16:44:19.000206Z

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”]

cgrand 2017-08-21T16:44:42.000236Z

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

pesterhazy 2017-08-21T16:50:23.000253Z

It flushes after 50ms?

cgrand 2017-08-21T17:19:44.000065Z

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

pesterhazy 2017-08-21T17:25:21.000041Z

it could lead to a noticeable lag when used interactively

pesterhazy 2017-08-21T17:27:14.000062Z

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

cgrand 2017-08-21T18:10:26.000070Z

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

cgrand 2017-08-21T18:12:47.000378Z

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

cgrand 2017-08-21T18:15:40.000165Z

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]

pesterhazy 2017-08-21T18:18:28.000064Z

I see

cgrand 2017-08-21T18:18:33.000207Z

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

pesterhazy 2017-08-21T18:18:52.000306Z

makes sense

pesterhazy 2017-08-21T18:19:00.000553Z

thanks for the explanation

cgrand 2017-08-21T18:20:00.000022Z

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

pesterhazy 2017-08-21T18:29:46.000094Z

upgrading unrepl to zork. Great idea

dominicm 2017-08-21T19:08:20.000335Z

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
cgrand 2017-08-21T20:23:51.000360Z

@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💰
pesterhazy 2017-08-21T20:30:37.000171Z

where can i preorder?