unrepl

discussing specification of an edn-based repl and its implementations.
plexus 2017-05-16T08:21:45.705715Z

^^ quick and dirty version of an Emacs unrepl client

pesterhazy 2017-05-16T08:23:14.727354Z

nice!!

plexus 2017-05-16T08:26:04.769444Z

note that this is literally the result of an evening+a morning hacking around, with little to no attempt to do things "right". Consider it alpha/experimental.

plexus 2017-05-16T08:26:13.771432Z

that said, do try it out of course 😉

pesterhazy 2017-05-16T08:26:15.771944Z

can you eval a buffer?

pesterhazy 2017-05-16T08:26:22.773699Z

trying it right now

plexus 2017-05-16T08:26:32.776177Z

no, no buffer or in-buffer eval stuff is there

pesterhazy 2017-05-16T08:27:31.790832Z

that would be cool at some point

pesterhazy 2017-05-16T08:28:13.801221Z

I'm curious if the features to display correct line/column etc work well for features

plexus 2017-05-16T08:28:21.803351Z

sending stuff to the process from a buffer would be easy to add, but to display results in the buffer instead of in the repl will require a lot more book keeping

pesterhazy 2017-05-16T08:28:51.810557Z

hm results in the minibuffer below like inf-clojure?

plexus 2017-05-16T08:29:08.814751Z

also no support for multiple repls/connections at this point, it just hard codes the repl buffer name

plexus 2017-05-16T08:29:51.825165Z

like I said this is a proof of concept, it will need a bit of reworking to better handle its state management

pesterhazy 2017-05-16T08:29:54.825965Z

sorry I'm a newb after 2 years of emacs, how do I load this?

plexus 2017-05-16T08:30:05.829163Z

yeah look at the comments at the bottom

plexus 2017-05-16T08:30:25.834507Z

do an M-x eval-buffer to load the code

plexus 2017-05-16T08:31:12.846707Z

then do (unrepl-start '("lein" "run" "-m" "unrepl.repl/start"))

plexus 2017-05-16T08:31:35.852463Z

or (unrepl-start '("telnet" "localhost" "3848")) if you're running on a socket repl

plexus 2017-05-16T08:31:44.854702Z

then switch to the *unrepl-repl* buffer

plexus 2017-05-16T08:32:29.866876Z

you can inspect the output coming from unrepl in the *unrepl-output* buffer

pesterhazy 2017-05-16T08:33:15.878794Z

ok

pesterhazy 2017-05-16T08:36:30.928381Z

had to start using this command

JAVA_OPTS="-Dclojure.server.unrepl={:address,\"127.0.0.1\",:port,3848,:accept,unrepl.repl/start}" lein run -m clojure.main/main

pesterhazy 2017-05-16T08:38:25.956687Z

also telnet didn't worked, I replaced it with nc instead

pesterhazy 2017-05-16T08:38:34.958903Z

which is better anyway I think

plexus 2017-05-16T08:38:36.959292Z

yeah that's what I did as well. the other should also work assuming it's in the right directory

pesterhazy 2017-05-16T08:38:54.963884Z

note the commas instead of spaces

pesterhazy 2017-05-16T08:39:03.966005Z

some weird quoting issue with lein I think

plexus 2017-05-16T08:39:25.971305Z

yeah nc is actually cleaner, I had to add some code to skip past telnet's initial output

plexus 2017-05-16T08:39:43.975798Z

ah interesting, must be a dfference in shell logic

pesterhazy 2017-05-16T08:39:50.977500Z

works ^^

plexus 2017-05-16T08:39:56.979047Z

I'm uzing zsh on linux

plexus 2017-05-16T08:39:59.979575Z

nice!

pesterhazy 2017-05-16T08:40:07.981744Z

here it's zsh on osx

plexus 2017-05-16T08:40:45.991388Z

anyway good to know, I'll try to get some kind of unrepl-jack-in working at some point

pesterhazy 2017-05-16T08:41:25.001456Z

hehe cool

pesterhazy 2017-05-16T08:41:44.006325Z

what unravel does is to send the unrepl payload on connect

pesterhazy 2017-05-16T08:42:18.015051Z

so you don't need to run a specially prepared repl, any socket server should do

plexus 2017-05-16T08:42:52.023942Z

ah I see, you upgrade a plain old repl to unrepl

plexus 2017-05-16T08:43:01.026219Z

makes sense

pesterhazy 2017-05-16T08:43:05.027147Z

right

pesterhazy 2017-05-16T08:43:32.034127Z

it's not perfect yet, because it uses a single ns so multiple connections could interfere if they use different unrepl versions

pesterhazy 2017-05-16T08:43:57.040211Z

but at some point we can gensym the ns name to rule out this possibility

plexus 2017-05-16T08:44:40.051279Z

now the next cool thing would be to install unrepl if it isn't there yet

pesterhazy 2017-05-16T08:45:38.066329Z

honestly I'd just ship the unrepl payload with the emacs module, so the two are always in sync

pesterhazy 2017-05-16T08:45:52.069773Z

at least for now

plexus 2017-05-16T08:46:41.082560Z

ah yeah so with "payload" you actually mean the full source, I was assuming you just sent a (unrepl.repl/start) but that makes sense

pesterhazy 2017-05-16T08:47:15.091187Z

right I mean the two src files (print.clj and repl.clj)

plexus 2017-05-16T08:47:42.098267Z

cool, I'll look into that

plexus 2017-05-16T08:47:53.101040Z

now onto some non-hobby hacking 😉

pesterhazy 2017-05-16T08:48:53.116252Z

really cool that your project shows than edn.el is up to the task

pesterhazy 2017-05-16T08:49:02.118465Z

that's what you use, right?

plexus 2017-05-16T08:49:05.119376Z

yes!

plexus 2017-05-16T08:49:33.126415Z

yeah works quite well. just have to get used to using vectors and hash-maps in elisp, you don't see those every day 🙂

pesterhazy 2017-05-16T08:49:35.126877Z

there was some worry about that project (bbatsov mentioned it wasn't maintained or something like that, I don't recall)

👍 1
plexus 2017-05-16T08:50:35.143441Z

no problems so far, obviously haven't really pushed its limits yet. but I assume edn is stable and well defined enough

pesterhazy 2017-05-16T08:50:54.148384Z

yeah well, not around the edges I think

pesterhazy 2017-05-16T08:51:02.150572Z

like what is a valid symbol char etc

pesterhazy 2017-05-16T08:51:12.153490Z

all those things are up for interpretation 🙂

plexus 2017-05-16T08:51:35.159557Z

right

pesterhazy 2017-05-16T10:29:05.636012Z

You mean the multiple namespaces for multiple clients?

dominicm 2017-05-16T10:42:08.805983Z

@pesterhazy to confirm my understanding from code: you mirror unrepl into your local project?

pesterhazy 2017-05-16T10:42:41.812938Z

Yeah for now

dominicm 2017-05-16T10:43:35.824430Z

@pesterhazy I think it's a good idea really. Although maybe with some vendoring for the ns.

cgrand 2017-05-16T10:45:09.844963Z

If you don’t mind I’ll explain what I have in mind out of the thread

cgrand 2017-05-16T10:46:27.861808Z

I believe that the general shape of unrepl is not going to change so it’s time to take care of other aspects, especially the upgrade from a standard repl (or from a nrepl)

cgrand 2017-05-16T10:48:20.885132Z

I’m going to add a helper ns to unrepl (well to the reference impl of unrepl 😉) for clients to perform the upgrade

cgrand 2017-05-16T10:50:04.906442Z

So a client won’t have to duplicate unrepl code unless it modifies it (and then yeah the ns should be changed)

pesterhazy 2017-05-16T11:53:31.704788Z

not sure I understand

pesterhazy 2017-05-16T11:53:43.707206Z

to my mind, shipping the unrepl code with the client is a virtue

pesterhazy 2017-05-16T11:54:05.711832Z

because it means that the target system doesn't need to be instrumented

cgrand 2017-05-16T11:54:09.712678Z

you ship it (as a dep not as a copy)

pesterhazy 2017-05-16T11:54:27.716351Z

ah yes, that makes sense

dominicm 2017-05-16T12:12:21.971778Z

shipping it as a dep means you become part of the jvm though, unravel is independent. So is cider/vim/etc.

cgrand 2017-05-16T12:22:40.124417Z

there are resources files, meant to be sent over the wire

pesterhazy 2017-05-16T12:23:40.139378Z

@dominicm, I can pull in the dependency as part of the unravel build process, no problem. Shouldn't affect users

dominicm 2017-05-16T12:35:25.327813Z

Hmm, I think I'm missing something. How does that work @pesterhazy?

pesterhazy 2017-05-16T12:40:03.404509Z

if you install unravel via npm install -g unravel-repl, that will include the repl.clj + print.clj from the unrepl repo

cgrand 2017-05-16T13:14:00.032196Z

# How to upgrade a repl to unrepl 

(Audience: repl client authors)

Once you have a connection to a REPL, you should send the content of the resource file `unrepl/upgrade/blob.clj` to it.

Once sent, start reading the output and wait for vectors. Here are the vectors you may get:
 * `[:unrepl.upgrade/require some.ns.name :some/thing]` then send the content of the ns identified by `some.ns.name` to the repl, followed by `:some/thing` to delimit the end of the ns.
 * `[:unrepl.upgrade/failed "pr-str of an exception"]` something went wrong you are back in the plain repl
 * `[:unrepl/hello ...]` success!
 * anything else should be ignored.

cgrand 2017-05-16T13:15:29.062042Z

So the blob performs an inversion of control to request missing resources to the client.

pesterhazy 2017-05-16T13:16:11.076429Z

I already do this

pesterhazy 2017-05-16T13:16:25.081580Z

I mean except for the error handling 🙂

cgrand 2017-05-16T13:16:47.088777Z

ok cool, anything you do that I don’t?

pesterhazy 2017-05-16T13:18:19.120475Z

I wait for the message [:unrepl/bye] and stop interpreting the stream as edn after that

pesterhazy 2017-05-16T13:19:15.139996Z

because in an upgraded repl, I get the regular user=> prompt after the end

cgrand 2017-05-16T13:20:42.170473Z

I only documented how to upgrade to unrepl (how to get to unrepl/hello), what happens after is not for UPGRADE.md

pesterhazy 2017-05-16T13:21:00.176687Z

DOWNGRADE.md

pesterhazy 2017-05-16T13:21:10.180401Z

🙂

pesterhazy 2017-05-16T13:21:27.186505Z

but seriously this is useful information

pesterhazy 2017-05-16T13:21:56.196498Z

can you elaborate on unrepl.upgrade/require?

pesterhazy 2017-05-16T13:22:02.198571Z

I can't quite parse that sentence

cgrand 2017-05-16T13:23:05.220916Z

ok so basically the blob upgrades to a short-lived “upgrade-repl” before upgrading to unrepl/start

cgrand 2017-05-16T13:23:48.235711Z

this “upgrade-repl” is the one emitting the :unrepl.upgrade/* messages

pesterhazy 2017-05-16T13:23:56.238462Z

why do we need this?

pesterhazy 2017-05-16T13:24:08.242848Z

I mean, we want to lower the barrier for tooling authors

pesterhazy 2017-05-16T13:24:38.252878Z

I thought it was great that @plexus could write a client in an evening

pesterhazy 2017-05-16T13:25:10.264804Z

so I think one thing that would be cool would be to aim to keep that and make additional features optional

cgrand 2017-05-16T13:26:40.296885Z

The other option was to systematically send the two files.

pesterhazy 2017-05-16T13:28:23.334155Z

Which files do you mean? repl.clj and print.clj?

cgrand 2017-05-16T13:50:41.840060Z

@pesterhazy yes these two

cgrand 2017-05-16T13:51:21.855874Z

JVM client can do:

(defn upgrade!
  “Upgrades a repl to unrepl. Upon success the first form read on the input stream should be
   a :unrepl/hello message.
   in is the input stream from the repl (so its *out*)
   out is the output stream to the repl (so its *in*)”
  [in out]
  (binding [*in* in
            *out* out]
    (-> “unrepl.upgrade.blob” resource slurp println)
    (loop []
      (let [x (read)]
        (case (when (vector? x) (first x))
          :unrepl.upgrade/require
          (let [[_ ns eom] x]
            (-> ns resource slurp println)
            (println eom))
          :unrepl.upgrade/success true
          :unrepl.upgrade/failed (throw (ex-info “Can’t upgrade.” {:reason (second x)}))
          (recur))))))

cgrand 2017-05-16T13:57:46.012558Z

@pesterhazy is your code for upgrading the repl simpler?

pesterhazy 2017-05-16T13:58:42.035957Z

hm I just use the string [:unrepl/hello as a sentinel

cgrand 2017-05-16T13:59:15.049757Z

you blindly send the two files? and wait for the sentinel?

pesterhazy 2017-05-16T14:00:00.068608Z

yeah exactly 🙂

cgrand 2017-05-16T14:03:27.160162Z

So what I propose is a replacement for <https://github.com/pesterhazy/unravel/blob/b687edb188aaab58b9ac0ce7a143172dc3e256a6/src/unravel/loop.cljs#L145-L147>

pesterhazy 2017-05-16T14:12:52.394609Z

cool

pesterhazy 2017-05-16T14:13:23.407549Z

yeah that was definitely just an ad hoc concatenation of the files

cgrand 2017-05-16T14:19:53.571327Z

maybe maybe maybe you are right

cgrand 2017-05-16T14:23:02.652733Z

I think I’m going to write a custom lein task to create the blob by concatenation

pesterhazy 2017-05-16T14:23:27.663436Z

sounds good. Does that handle renaming the namespaces too?

cgrand 2017-05-16T14:23:38.667671Z

why rename?

cgrand 2017-05-16T14:23:51.673140Z

(if you don’t change them :-))

pesterhazy 2017-05-16T14:24:21.685883Z

to avoid conflicts between, say, unravel and emacs's unrepl-mode?

cgrand 2017-05-16T14:25:14.708267Z

there’s no conflict if none patched unrepl

cgrand 2017-05-16T14:25:20.711022Z

did you patch it?

pesterhazy 2017-05-16T14:25:49.723161Z

I did (to add a :unrepl/bye action), but that can be easily upstreamed

pesterhazy 2017-05-16T14:26:06.730775Z

but inevitably at some point two clients will use different versions of unrepl, no?

pesterhazy 2017-05-16T14:26:19.736690Z

so they might overwrite each other

pesterhazy 2017-05-16T14:27:56.779035Z

creating a race conditions - who connects last wins

cgrand 2017-05-16T14:27:56.779260Z

there are two cases to consider: • two different versions of unrepl (one older than the other) • two forks

cgrand 2017-05-16T14:30:26.845439Z

First case I can deal with it in the blob (and developing unrepl with no breaking change)

pesterhazy 2017-05-16T14:30:50.856026Z

hm yeah but that seems like you're creating more work for yourself

pesterhazy 2017-05-16T14:31:00.860511Z

instead of just having two independent versions of unrepl

cgrand 2017-05-16T14:31:01.860687Z

Second case: bad bad forker & unrepl should provide ways to be extended without forking (eg for actions)

cgrand 2017-05-16T14:31:20.869301Z

I don’t know

pesterhazy 2017-05-16T14:31:30.873515Z

I agree with backward-compatibility as a goal

cgrand 2017-05-16T14:31:33.874946Z

maybe yes I should just rename for now on

cgrand 2017-05-16T14:31:49.881685Z

and consider a more satisfying solution later

pesterhazy 2017-05-16T14:31:58.885833Z

that sounds like a good plan

cgrand 2017-05-16T14:32:20.896102Z

or I can spend all my time devising a clever solution (which qualifies as an advanced form of procrastination)

pesterhazy 2017-05-16T14:32:24.897380Z

maybe unrepl turns out to be super stable, so we realize soon enough those worries are all unfounded

pesterhazy 2017-05-16T14:38:37.057864Z

some of those advanced procrastination experiments have already yielded fantastic results, like the ellipsis feature 🙂

richiardiandrea 2017-05-16T15:09:52.897325Z

Cool Emacs mode 😀😀

richiardiandrea 2017-05-16T15:11:25.937874Z

Starring the file

cgrand 2017-05-16T15:47:14.868647Z

@pesterhazy congrats for scoring an unravel talk at euroclojure

pesterhazy 2017-05-16T16:27:08.853247Z

thanks 🙂

pesterhazy 2017-05-16T16:27:36.863776Z

are you coming for the conference @cgrand? how about everyone else?

pesterhazy 2017-05-16T16:28:04.874894Z

it might be nice to get an unrepl unsession organized 🙂

cgrand 2017-05-16T16:28:07.876040Z

Unlikely

richiardiandrea 2017-05-16T16:57:39.544955Z

@pesterhazy I am going to be there

pesterhazy 2017-05-16T17:02:51.669833Z

@richiardiandrea nice!

richiardiandrea 2017-05-16T17:05:14.725560Z

still trying to find the time to work on a universal js/cljs repl...man, I wish I had some bureaucracy to take care of 😄

dominicm 2017-05-16T17:57:47.925312Z

I'll be there

dominicm 2017-05-16T17:59:00.954537Z

@pesterhazy @cgrand something we could adopt, is that there's some build process for mangling the ns of the unrepl clj files during build. Then it would be unique for each project: no clobbering

pesterhazy 2017-05-16T18:19:46.440357Z

@richiardiandrea structured procrastination eh?

pesterhazy 2017-05-16T18:20:05.447786Z

we should meet up in Berlin when you guys are here and talk repls 🙂

richiardiandrea 2017-05-16T18:20:24.455393Z

oh that's for sure

richiardiandrea 2017-05-16T18:20:25.455820Z

😄

pesterhazy 2017-05-16T18:20:56.466377Z

@dominicm, yeah I think that would be a good option

dominicm 2017-05-16T18:31:37.724272Z

Vital.vim does this, which I find infinitely curious

plexus 2017-05-16T20:19:11.077209Z

cgrand: third case : you're working on unrepl itself over an unrepl session, and want to change things without interfering your running session