unrepl

discussing specification of an edn-based repl and its implementations.
cgrand 2017-04-03T07:35:53.511585Z

@thheller thanks, logging was a blatant oversight. so we can imagine log messages like [:log [“ns.or.class.or.whatever.hierarchical.key.as.string” inst & args] group-id?] now there’s the issue of bridging to any combination of timbre/jul/log4j/logback/slf4j/...

thheller 2017-04-03T07:39:48.554758Z

@cgrand yeah, yet another logging lib probably isn't gonna see adoption

thheller 2017-04-03T07:40:07.558484Z

but getting to the internals of all is not gonna be easy/possible

thheller 2017-04-03T07:40:57.568209Z

but as a first step we just need a (log/spy my-value)

thheller 2017-04-03T07:42:48.589354Z

but :log is also more system-wide really

thheller 2017-04-03T07:43:13.593829Z

so having them as generic messages on a REPL connection might not make sense as they are not like :out

thheller 2017-04-03T07:43:48.600590Z

instead you could do open a new REPL connection and call (log/tail maybe-some-kind-of-filter)

thheller 2017-04-03T07:44:30.608820Z

which then upgrades that connection into a suitable (not strictly unrepl) protocol

thheller 2017-04-03T07:45:05.615691Z

so the connection is specialized to only log related commands and not a generic REPL anymore

thheller 2017-04-03T07:46:05.627478Z

I don't feel like connecting to a REPL, upgrading a unrepl and then getting a bunch of log messages just because the system is logging is useful

thheller 2017-04-03T07:46:23.631135Z

you should be asking for it

cgrand 2017-04-03T07:46:36.633496Z

I agree that indeed redirecting all logs by default to the repl is a bad idea

cgrand 2017-04-03T07:47:08.639566Z

(as capturing System wide out and err by default)

thheller 2017-04-03T07:47:49.647607Z

as you said

thheller 2017-04-03T07:47:53.648315Z

> REPL: the ultimate content negotiation protocol!

thheller 2017-04-03T07:48:12.651874Z

so don't try to force everything into one protocol

thheller 2017-04-03T07:48:16.652797Z

specialize instead

cgrand 2017-04-03T07:49:15.664497Z

but no need to overspecialize either: open another unrepl session, activate log there and use its input for resolving log-related elisions

thheller 2017-04-03T07:49:37.668681Z

but log becomes bi-directional if you want the elision part

thheller 2017-04-03T07:50:07.674644Z

and I think everything is strictly RPC in that sense

thheller 2017-04-03T07:50:31.679687Z

which is harder to realize in a streaming protocol

thheller 2017-04-03T07:51:33.691847Z

I do think it is valuable to have /some/ messages also appear in the REPL

thheller 2017-04-03T07:52:08.698487Z

ie. [:log "some-label" some-unique-id]

thheller 2017-04-03T07:52:20.700699Z

so the REPL window can show that something was logged

thheller 2017-04-03T07:52:39.704631Z

it can then take the id and open a new connection to interact with the actual log message

thheller 2017-04-03T07:52:46.706058Z

should the user click on that

thheller 2017-04-03T07:53:50.719346Z

I'm really used to the js/console.log style logging and just dumping everything in there

thheller 2017-04-03T07:54:01.721586Z

should I want to expand even huge values I can

thheller 2017-04-03T07:54:12.723737Z

most of the time I don't though

cgrand 2017-04-03T07:58:19.773805Z

yeah but we really have to “decomplect” REPL-the-UI and REPL-as-pair-of-streams.

cgrand 2017-04-03T07:59:08.784276Z

To sustain a good REPL ui, we need at least 2 REPLs-as-streams (main & control)

cgrand 2017-04-03T08:00:30.803799Z

having log messages using either the control link or a dedicated link is a suitable option

thheller 2017-04-03T08:00:53.809224Z

I don't agree

cgrand 2017-04-03T08:00:57.810284Z

it’s to the UI to merge some or all of logs messgae back in the “console"

cgrand 2017-04-03T08:01:00.811081Z

on which point?

thheller 2017-04-03T08:01:10.813290Z

what we see as the user is only one stream

thheller 2017-04-03T08:01:43.820964Z

that actions we do go over a second connection (or third) is an implementation detail

thheller 2017-04-03T08:02:43.834729Z

so having the :log messages appear in the first stream only helps to establish context

thheller 2017-04-03T08:03:37.846255Z

ie. this log happened in a thread I started in this REPL after :start-eval

thheller 2017-04-03T08:04:04.851804Z

but I might also want a dedicated window somewhere

thheller 2017-04-03T08:04:10.853008Z

that just tails the log in the background

thheller 2017-04-03T08:04:53.862036Z

which I can sometimes look at, maybe click around on a webpage while some log accumulates in the background

thheller 2017-04-03T08:05:00.863639Z

and then look at the log

thheller 2017-04-03T08:05:14.866331Z

that has no REPL interaction at all, I want a dedicated window for that

thheller 2017-04-03T08:05:37.871495Z

that we used a REPL to bootstrap this log tail is just an implementation detail. it is not a REPL.

thheller 2017-04-03T08:08:37.910182Z

it cannot eval for example, so we don't need interrupt and other things

thheller 2017-04-03T08:09:13.917787Z

(although you might add eval as a log protocol command, but that is not a REPL eval, it cannot read)

thheller 2017-04-03T08:11:18.944939Z

I have no idea how all this would work in emacs since it is a bit limited UI-wise

thheller 2017-04-03T08:11:49.951650Z

but something like Cursive could provide very fancy dedicated UI elements

thheller 2017-04-03T08:12:35.961698Z

when I find the time to do so I will eventually implement some kind of solution for the browser

thheller 2017-04-03T08:13:48.976981Z

just to be able to play with the UI a bit

thheller 2017-04-03T08:14:24.984847Z

I'm too spoiled by the chrome devtools, even having something basic like its console for CLJ would be amazing

cgrand 2017-04-03T09:57:09.437646Z

tools.logging assumes strings and uses pr-str

cgrand 2017-04-03T10:04:31.544763Z

timbre is ok

cgrand 2017-04-03T13:11:15.012230Z

So (map #(/ %) (iterate dec 3)) now machine prints as

(<#C4C63FWP5|unrepl>/ratio [1 3] <#C4C63FWP5|unrepl>/ratio [1 2] 1 <#C4C63FWP5|unrepl>/lazy-error #error {:cause "Divide by zero", :via [{:type <#C4C63FWP5|unrepl>.java/class java.lang.ArithmeticException, :message "Divide by zero", :at <#C4C63FWP5|unrepl>/object [#unrepl.java/class java.lang.StackTraceElement "0x272298a" "clojure.lang.Numbers.divide(Numbers.java:158)"]}], :trace [#unrepl/... nil]})
and the client can render it as (1/3 1/2 1 💩)

pesterhazy 2017-04-03T13:17:52.134201Z

Emojis? You're getting me ideas

cgrand 2017-04-03T13:18:27.144821Z

that reco was kind of tongue-in-cheek

pesterhazy 2017-04-03T13:18:38.148177Z

Well the damage is done

pesterhazy 2017-04-03T13:18:54.153220Z

I can't unthink emojis now

cgrand 2017-04-03T13:19:10.158018Z

what if code use emojis?

cgrand 2017-04-03T13:19:24.162810Z

they are legal clojure symbols

pesterhazy 2017-04-03T13:19:45.169092Z

Heh

cgrand 2017-04-03T13:31:58.412Z

@thheller something like:

(let [^ch.qos.logback.classic.Logger logbackroot (org.slf4j.LoggerFactory/getLogger ch.qos.logback.classic.Logger/ROOT_LOGGER_NAME)]
  (.addAppender logback
    (proxy [ch.qos.logback.core.AppenderBase] []
      (doAppend [^ch.qos.logback.classic.spi.ILoggingEvent e]
        (when *repl-issued* ; does not exist yet
          (unrepl/write [:log (into [(keyword (.getLevel e)) ; todo normalize
                                    (.getTimeStamp e) ; todo to inst
                                    (.getMessage e)] ; todo split and interleave with arguments
                                (.getArgumentArray e))]))))))

cgrand 2017-04-03T13:38:24.541468Z

(there should be variants for all commons backend, and try them all)

cgrand 2017-04-03T13:38:36.545533Z

https://imgflip.com/i/1mlf3u

thheller 2017-04-03T13:38:46.548793Z

neat if you can get access to that stuff

dominicm 2017-04-03T13:55:03.900159Z

I'm a little confused by the actions. How do I send them?

cgrand 2017-04-03T13:56:16.928673Z

@dominicm in general or for log stuff in particular?

dominicm 2017-04-03T13:56:35.936081Z

@cgrand In general.

cgrand 2017-04-03T13:58:03.969927Z

1/ you substitute parameters (if any) 2/ you send the resulting form to a repl

cgrand 2017-04-03T13:58:44.985703Z

(in general another repl)

dominicm 2017-04-03T13:59:33.004817Z

@cgrand So I type a literal: [:exit] for example?

cgrand 2017-04-03T14:03:51.111039Z

Hmm no the idea is that the client UI has some capabilities

cgrand 2017-04-03T14:04:33.126923Z

and they are enabled or disabled depending on what’s happening over the wire

cgrand 2017-04-03T14:05:30.148600Z

(to reduce the coupling between the UI and the upgrage blob/unrepl impl (eg a CLJS unrepl would not offer the same actions as a CLJ one)

cgrand 2017-04-03T14:06:06.161988Z

so you get the :hello and it lists :exit and you client has some UI for :exit

cgrand 2017-04-03T14:06:46.177156Z

so you take the form mapped to :exit and you send it (generally through a second repl linkà

dominicm 2017-04-03T14:16:03.390831Z

Hmm, still not fully understanding what is listening to the form mapped to :exit

dominicm 2017-04-03T14:16:09.392911Z

& why it can't be sent manually.

cgrand 2017-04-03T15:32:05.247792Z

@dominicm : First term

$ telnet localhost 5555
user=&gt; (require 'unrepl.repl)(unrepl.repl/start)
nil
[:unrepl/hello {:session :session282, :actions {:exit (unrepl.repl/exit! :session282)}}]
[:prompt {}]
[:prompt {}]
Second term:
$ telnet localhost 5555
user=&gt;(unrepl.repl/exit! :session282)
true
First term:
Connection closed by foreign host.
~$

dominicm 2017-04-03T15:33:12.275385Z

I hadn't realised that was what the forms looked like in :actions. That makes loads of sense now.

dominicm 2017-04-03T15:33:48.290186Z

And I suppose that theoretically I could send the (unrepl.repl/exit! …) from the first term, even if that's not the intent?

cgrand 2017-04-03T15:34:12.299439Z

If the main repl is not in a dirty state (busy or in the middle of a form), suicide is possible:

user=&gt; (require 'unrepl.repl)(unrepl.repl/start)
nil
[:unrepl/hello {:session :session292, :actions {:exit (unrepl.repl/exit! :session292)}}]
[:prompt {}]
[:prompt {}]
(unrepl.repl/exit! :session292)
[:started-eval {:actions {:interrupt (unrepl.repl/interrupt! :session292 1), :background (unrepl.repl/background! :session292 1)}} 1]
Connection closed by foreign host.

cgrand 2017-04-03T15:35:29.331286Z

Heh, just read your last message. Great minds think alike

😜 1
dominicm 2017-04-03T15:35:52.340525Z

Okay, this is great.

dominicm 2017-04-03T15:36:57.365770Z

I fear Slack might be misrepresenting my location for you to send that 😛

dominicm 2017-04-03T15:37:46.384941Z

Is the plan for unrepl to handle multi-plexing still?

cgrand 2017-04-03T15:38:04.391929Z

no I mixed you and @pesterhazy locations

cgrand 2017-04-03T15:39:23.423240Z

re: multiplexing, no. One can provide a multiplexing layer but unrepl-the-spec is independent of it.

dominicm 2017-04-03T15:41:37.477010Z

I thought not, just confirming. I was trying to plan how I'd do my multi-hop funkage to stations in the wild. I'll have to have a think about what a multi-plexing layer might look like.

cgrand 2017-04-03T15:41:37.477204Z

That was the whole point of going to multiple connections, not having to bake multiplexing in

cgrand 2017-04-03T15:42:25.496588Z

a multiplexing layer may be quite simple as long as you don’t want to handle backpressure

dominicm 2017-04-03T15:45:12.562941Z

Curious: Is there a mechanism for custom actions yet?

cgrand 2017-04-03T15:45:30.570480Z

give me an example

dominicm 2017-04-03T16:03:55.025645Z

Interested in how the find-usages in refactor-nrepl would be ported. It's a particularly slow operation, so you want to "stream" the values as you get them (channel-like semantics).

cgrand 2017-04-03T16:07:13.104125Z

Does it need to be an action? Can't it be something running on a dedicated repl or on a thread spawned from the control repl?

dominicm 2017-04-03T16:07:54.120450Z

My thoughts behind action was having it generate custom tags, so if that can be done without a custom action, that is okay too.

dominicm 2017-04-03T16:08:31.134428Z

Essentially I'm looking for something like [:out] but for results (like [:eval])

dominicm 2017-04-03T16:10:24.176762Z

I'm not sure I've explained that well. If I do:

(do (println 1) (Thread/sleep 10000) (println 2))
I get
[:out "1"]
;; some time later…
[:out "2"]
[:eval nil]
I want to try do something like:
[:streamed 1]
;; sleep
[:streamed 2]
[:eval nil]

thheller 2017-04-03T16:10:25.177509Z

> find-usages in refactor-nrepl

thheller 2017-04-03T16:10:32.180083Z

this is not a REPL interaction

dominicm 2017-04-03T16:10:51.186828Z

@thheller not sure I understand?

thheller 2017-04-03T16:12:04.213936Z

REPL is read-eval-print-loop

thheller 2017-04-03T16:13:42.249617Z

your example would be better served by a custom protocol

thheller 2017-04-03T16:14:07.258601Z

but that protocol has different concerns than a REPL would have

thheller 2017-04-03T16:14:13.260613Z

ie :streamed

thheller 2017-04-03T16:14:36.269675Z

but it doesn't need some of the other things unrepl provides anymore

thheller 2017-04-03T16:15:03.279438Z

no read no :prompt ...

cgrand 2017-04-03T16:18:49.364179Z

@dominicm between eval and out there may be log

thheller 2017-04-03T16:21:11.417199Z

didn't bring this up in a while https://github.com/Microsoft/language-server-protocol 😉

thheller 2017-04-03T16:21:44.429933Z

something like that should be used for tooling stuff .. it also has find-usages built-in

thheller 2017-04-03T16:43:39.913059Z

@dominicm this probably doesnt make much sense

thheller 2017-04-03T16:43:43.914276Z

&gt; {:id 1 :method "textDocument/references" :params {:textDocument {:uri "foo.clj"}
                                                    :position {:line 1 :character 2}
                                                    :context {:includeDeclaration true}}}
&lt; {:id 1 :result [{:uri "foo.clj" :range {:start {:line 1 :character 2} :end {:line 1 :character 6}}}]}

thheller 2017-04-03T16:43:58.919880Z

but in essence this is what it would look like for the lang server protocol

thheller 2017-04-03T16:44:10.923983Z

note that this a sync RPC request

thheller 2017-04-03T16:44:42.935608Z

you could also do it in a streaming fashion although the protocol doesn't define that

thheller 2017-04-03T16:45:10.945771Z

it does however define {:method "$/cancelRequest" :params {:id 1}} to cancel the reference request

thheller 2017-04-03T16:45:58.963684Z

you may also send notifications while processing the request

thheller 2017-04-03T16:46:15.969438Z

you can do anything really. the protocol itself is fairly simple

thheller 2017-04-03T16:46:42.979002Z

its just JSON-RPC the lang server protocol just tries to define a standard protocol for editors

thheller 2017-04-03T16:46:56.983676Z

whether or not that will catch on permanently remains to be seen

thheller 2017-04-03T16:47:01.985610Z

looks promising though

thheller 2017-04-03T16:47:19.991789Z

but none of that is related to a REPL and should not be confused with it

dominicm 2017-04-03T17:06:00.407522Z

I like the way cider looks at these operations as plain clojure functions. They're essentially helper functions, that tooling is able to interpret the result of.

thheller 2017-04-03T17:07:02.430218Z

thats the point of the language server as well

thheller 2017-04-03T17:09:55.489812Z

I want to work on my lang server impl a bit soon

thheller 2017-04-03T17:10:18.497995Z

I expect to use some of the tools behind cider-nrepl, I really don't wnat to write all that stuff by myself 🙂

thheller 2017-04-03T17:10:30.502153Z

just not the nrepl bits

dominicm 2017-04-03T17:49:59.368985Z

https://github.com/autozimu/LanguageClient-neovim cool 😄 Get LanguageServer working then 😉

dominicm 2017-04-03T17:57:53.550066Z

@thheller would you build it upon unrepl? Or something else?

thheller 2017-04-03T18:00:23.608825Z

probably not unrepl no

thheller 2017-04-03T18:01:21.631519Z

well you could start it unrepl and it will upgrade the connection

thheller 2017-04-03T18:01:59.645642Z

but currently I think thats much more complicated than just connecting to a dedicated port

thheller 2017-04-03T18:02:13.650434Z

FWIW the language server is working

thheller 2017-04-03T18:02:29.656957Z

it just doesn't ahve many features yet but the protocol is implemented

thheller 2017-04-03T18:03:48.687501Z

only got to the proof-of-concept stage so far

thheller 2017-04-03T18:04:09.695368Z

but when you open a .cljs file it will compile that file and publish some diagnostics

thheller 2017-04-03T18:04:15.697486Z

ie. warnings in that file

thheller 2017-04-03T18:05:40.731953Z

this is from Visual Studio Code, no idea how that would work in neovim but if that language client support textDocument/didOpen and publishDiagnostics it probably just works 😉

pesterhazy 2017-04-03T18:29:59.274397Z

Published unravel 0.1.4, with tab completion and prettier exceptions

pesterhazy 2017-04-03T18:59:54.937387Z

@cgrand I'm wondering how to track evals

pesterhazy 2017-04-03T19:00:37.954136Z

for tab completion I send out a command and get back [:eval '(filter filterv first) 1]

pesterhazy 2017-04-03T19:01:02.963105Z

but how do I know which number I should be looking for?

cgrand 2017-04-03T19:01:06.964511Z

@pesterhazy elaborate

pesterhazy 2017-04-03T19:01:15.967997Z

currently I just count how many forms I've sent over the wire

pesterhazy 2017-04-03T19:01:24.971367Z

but that seems hacky?

cgrand 2017-04-03T19:01:48.979168Z

Right after the form you receive a started-eval msg

pesterhazy 2017-04-03T19:02:19.990653Z

ah I'm still on the older version as of ~5 days ago

pesterhazy 2017-04-03T19:02:32.995537Z

so I should listen for started-eval and compare that to what I sent out?

pesterhazy 2017-04-03T19:02:51.002100Z

so I learn the eval-id?

cgrand 2017-04-03T19:08:37.122169Z

There's an echo message I haven't implemented yet

cgrand 2017-04-03T19:10:28.159634Z

But echo is more meant to map evals to expression when they are batched.

pesterhazy 2017-04-03T19:14:28.238531Z

Ideally I would be able to send (with-eval-id #uuid "e35a9235-0517-4017-b9f3-d16b76e5cc44" (get-completions)) and then map the result back to that

cgrand 2017-04-03T19:19:37.342826Z

Why can't you do that? Put the application id in the return value and you are done.

pesterhazy 2017-04-03T19:21:15.375825Z

sure I can