This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-03-13
Channels
- # arachne (2)
- # architecture (23)
- # bangalore-clj (5)
- # beginners (35)
- # boot (79)
- # cider (6)
- # cljs-dev (34)
- # cljsrn (9)
- # clojure (164)
- # clojure-argentina (2)
- # clojure-austin (4)
- # clojure-italy (7)
- # clojure-russia (40)
- # clojure-serbia (1)
- # clojure-spec (76)
- # clojure-uk (36)
- # clojurescript (47)
- # cursive (14)
- # datascript (2)
- # datomic (8)
- # dirac (19)
- # emacs (29)
- # heroku (7)
- # hoplon (35)
- # jobs-rus (1)
- # juxt (2)
- # leiningen (1)
- # lumo (23)
- # mount (4)
- # off-topic (22)
- # om (16)
- # onyx (19)
- # parinfer (10)
- # pedestal (47)
- # proton (5)
- # re-frame (88)
- # rum (1)
- # spacemacs (33)
- # sql (29)
- # uncomplicate (1)
- # unrepl (131)
- # untangled (5)
- # yada (12)
Demo of the “futurize current eval” command (here mapped to ^Z
) I pushed hours ago:
[:prompt {:cmd false, clojure.core/*ns* <#C4C63FWP5|unrepl>/ns user, clojure.core/*warn-on-reflection* nil}]
(do (Thread/sleep 10000) 42)
Oh! It’s taking too long!
^Z
[:eval <#C4C63FWP5|unrepl>/object [#unrepl.java/class java.util.concurrent.FutureTask "0x68f1b17f" "java.util.concurrent.FutureTask@68f1b17f"] 4]
The current evaluation was backgrounded and a future returned.
Wait a little and deref it:
(deref *1)
[:eval 42 5]
@cgrand cool, but just like the linux equiv I usually open another tab and let the other one run undisturbed
since that way I will be "notified" when the command finishes, opposed to having to check for it
Sending (future …)
(or a/go
or a/thread
) (as a user) is always a possibility. However we don’t too that too often. It would be cumbersome to do that constantly. Here the intent is really “fix my blunder” (same spirit as : interrupt
) not generic bg-eval.
So re:sequential exec I believe that the REPL should not add concurrency of its own, all concurrency should happen at the lang level (or put in other words: requested by the user)
https://docs.oracle.com/javase/8/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html
yeah given the stance of backwards compatibility for the JVM it will probably never be removed
but letting the REPL interrupt itself implies that everything must be eval
ed in its own thread
if you open another connection and somehow could access the other loop (hint shadow.repl
) you could interrupt via the other connection
otherwise you can't really interrupt something when the user disconnects from the REPL while something is running
From a spec point of view it means going from a spec that assumes the existence of the connection to a spec that talks about setting up connections. Your modus operandi for interruption would broadens the scope of the spec.
yes, everything shadow.repl
related assumes that the server was started in a shadow.repl
aware fashion
Speaking of EOF when stdin is connected to a term and you hit ^D
(not escaped, handled by the term) then .read
returns -1 but subsequent reads work.
>> It follows that some tooling needs (e.g. autocompletion) may be better serviced by a separate connection which may not be a REPL (but may have started as a REPL upgraded to something else).
[:unrepl/hello {:commands {:interrupt <#C4C63FWP5|unrepl>/raw \u0003, :exit <#C4C63FWP5|unrepl>/raw \u0004, :background-current-eval <#C4C63FWP5|unrepl>/raw \u001a, :set-source <#C4C63FWP5|unrepl>/raw [\u0010 <#C4C63FWP5|unrepl>/edn (set-file-line-col <#C4C63FWP5|unrepl>/param :unrepl/sourcename <#C4C63FWP5|unrepl>/param :unrepl/line <#C4C63FWP5|unrepl>/param :unrepl/column)], :unrepl.jvm/enable-sideloader <#C4C63FWP5|unrepl>/raw "\u0010(enable-sideloader)"}}]
[:prompt {:cmd false, clojure.core/*ns* <#C4C63FWP5|unrepl>/ns user, clojure.core/*warn-on-reflection* nil}]
`
Ask for a class:
unknown.Clazz
[:exception #error {:cause "unknown.Clazz", :via [{:type <#C4C63FWP5|unrepl>.java/class clojure.lang.Compiler$CompilerException, :message "java.lang.ClassNotFoundException: unknown.Clazz, compiling:(unrepl-session:1:165)”,
[.....]
<#C4C63FWP5|unrepl>/... {:get <#C4C63FWP5|unrepl>/raw "\u0010(... G__228)"}]} 1]
[:prompt {:cmd false, clojure.core/*ns* <#C4C63FWP5|unrepl>/ns user, clojure.core/*warn-on-reflection* nil}]
Now, let’s send the :unrepl.jvm/enable-sideloader
command:
^P(enable-sideloader)
[:command :sideloader-enabled 2]
[:prompt {:cmd false, clojure.core/*ns* <#C4C63FWP5|unrepl>/ns user, clojure.core/*warn-on-reflection* nil}]
And try again asking for the class
unknown.Clazz
[:unrepl.jvm/find-class “unknown.Clazz”]
after this message the REPL waits for either nil
or for the bytecode as a base64 encoded string.
nil
[:exception ... 3]
but that seems like something tooling vs REPL. is that something the user would ever type by hand?
ah missed that part .. the server is asking the client for a class that the server cannot find
hmm but how would that work if you run into something that actually requires a bunch of classes?
I don't know ... I'm not convinced that bootstrapping the entire thing over a REPL is a good idea in the first place
how would you handle version conflicts? the server has guava v15 but the client expects guava v21 or so
I assume its goal is to let tools ship "extensions" without adding that extension to the classpath of the server runtime?
On a remote JVM because you are trying to figure out something happening in one environment but not locally? "Open a ssh tunnel for the repl and code like it’s local" <- that’s the intent
the assumption is that you connected to a socket, whether thats local or remote makes no difference
yes and I believe it’s more convenient to have them shipped on demand by the repl client than scp-ing them on the server class path (and remembering to clean of after)
but you could just send (unrepl.jvm/load-this-jar-please "cool.jar" <jar-contents>)
?
so you’d rather reimplement the deps resolution mechanism on the tool side (with the risk of getting it slightly wrong or even totally wrong if the user code relies on reflection, explicit calls to class loader) rather than rely on the real thing.
It’s about 20 loc: 10 for a stub ClassLoader in Java (which delegates findClass to a fn) and 10 for the fn
'enable-sideloader
(fn []
(var-set clojure.lang.Compiler/LOADER
(unrepl.ClassLoader. (var-get clojure.lang.Compiler/LOADER)
(fn [name]
(when @unrepl
(write [:unrepl.jvm/find-class name])
(with-bindings {in-eval false}
(when-some [base64-encodedclass (read)]
(base64-decode base64-encodedclass)))))))
:sideloader-enabled)
the server runtime has MyClass.someFn(a,b,c)
loaded but the code you injected uses MyClass.someFn(a)
?
I remain skeptical, seems to me like it would be way too easy to shoot yourself in the foot
This sideloader implementation is most certainly a bad idea. • The inversion of control between server and client may happens while the client as partially sent a form so the state of the PushbackReader may be dirty, which means more logic to track that. • if the repl has been upgraded it doesn’t work anymore.
All saner implementations seem to imply a repl connection and a request/response connection.
at least from looking at it the (.read *in*)
would still have (something-else)
left to read before getting to the ^Z
do I? I meant that I entered (some-long-running-task) (something-else)
and it went over the wire, then I realize that it takes a long time and want to bg it
since its a streaming protocol, the ^Z
will not be processed until after (some-long-running-task)
finishes?
it sucks when load-file
fails because in-ns
still goes through, but every repl has that issue
How ok but should the client send the second form before having acknowledged processing of the previous one?
Why queue on the server (in the connection buffer) rather than on the client? Honest question
it implies that the client knows how to separate the text the user is typing into tokens
It reminds of two commands I wanted to explore: :enable-echo (to know how the reader cut the input and matching eval result with a chunk of input)
Thinking of things like inf clojure and neoterm, which send the current file/selection to a terminal naively, I can see the potential for confusion.