This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-12-19
Channels
- # adventofcode (82)
- # beginners (70)
- # boot (34)
- # boot-dev (13)
- # cider (45)
- # clara (4)
- # cljs-dev (3)
- # cljsrn (2)
- # clojure (91)
- # clojure-art (8)
- # clojure-czech (1)
- # clojure-dusseldorf (3)
- # clojure-france (11)
- # clojure-germany (1)
- # clojure-greece (39)
- # clojure-hamburg (1)
- # clojure-italy (24)
- # clojure-norway (2)
- # clojure-spec (7)
- # clojure-uk (31)
- # clojurescript (56)
- # core-async (7)
- # cursive (8)
- # data-science (10)
- # datomic (41)
- # duct (7)
- # emacs (1)
- # events (1)
- # fulcro (83)
- # graphql (6)
- # klipse (1)
- # leiningen (28)
- # lumo (67)
- # off-topic (14)
- # om (9)
- # onyx (3)
- # perun (4)
- # re-frame (22)
- # reagent (11)
- # ring-swagger (2)
- # rum (1)
- # specter (46)
- # sql (13)
- # uncomplicate (17)
- # unrepl (114)
@cgrand I wish that I would understand better the mecahnics of upgradeable repl. I'm wondering if these are related, for example in inf-clojure
which I use with lumo, I've been constantly trying to make some loops I have running use process.nextTick()
to be appended in front of the js eventqueue. Long story short, every evaluation of get-arglists and get-completions, needs to be read and cycle trough many functions before evaluated, causing the event-queue of the single thread to fill up (and seems that setImmediate
and process.nextTick()
become meaningless in those cases). An alternative, which if I understand you with the concept of upgradeable repl, would be that functions like get-completions and get-arglists, could potentially not be read, but just become a function call from the socket repl, where the socket repl could have logic from a front end to call a compiled function or send it trough the reader (evaluate-text etc)?
(unrelated), you mention a callback before printing, how would the user not specify a special callback, how would a front-end "take over" a repl. Via runtime variable?
@hlolli thanks for your interest, first let me define what I mean by REPL: “read eval print loop” and nothing else. get-completion
for example don’t belong to the REPL but to the client (for lumo since the client and the REPL share the same process it’s blurry). Try the socket repl of lumo to get a “pure” repl.
@cgrand Yes I agree, it shulould not belong to the repl therefore I think this part here is not very cool https://github.com/clojure-emacs/inf-clojure/blob/master/inf-clojure.el#L828-L831
~$ clojure
Clojure 1.9.0
user=> (loop [] (let [x (read)] (when-not (= :bye x) (println “you said” x) (recur))))
hello
you said hello
world
you said world
help I’m trapped!
you said help
you said I’m
you said trapped!
:bye
nil
user=>
It’s the same meaning of upgrade as in “HTTP 101”: before it was http now it’s something else
and this is what unrepl exploits to get you a better repl experience starting from a plain repl
it's a sugar for promises, but really blocks the reset of the body until returned, given the whole function is noted with async.
in cljsjs my best proposal so far is to have the expression evaluates to a SuspensionRequest
value (which is just a special delay)
yes I guess so, it would be more akin to CPS. If you need real block, then I guess it wont cut the butter.
I don’t want real block, because JS is async, I just want to get the same feature at the end: the possibility for an expression to take over the repl. I don’t care if the expression is different than in Clojure.
I could see a new function for this in lumo, a callback is provided and starts a read-eval-print-loop
(again it’s doable I’ve made three or four protototypes) what I want to find now is how to minimally modify lumo built-in repl to give it this power.
For a long time the design was variations over “set a callback using a public fn or global object”. I was never happy with that because it’s a callback setting that has an additional side-effect: suspend the main loop and then you get a lot of special cases: - what should I do of the return value (or the exception thrown) of the whole expression? - what if the user sets the callback twice? can’t start two loops, so: first wins? last wins? exception? - what if the user sets the callback from a really async callback? Should not stop the loop obviously, throw an exception and hope the exception will get properly reported at some point?
So it makes the contract “not great” and the implementation is more complex (because more checks).
The expression must evaluate to a suspension request so it provides easy answers to all the points above because it’s just a value (containing a function).
When the lumo repl gets a suspension request out of evaluation then instead of printing it and “looping”, it should call the function in the suspension request, and pass it the input stream and a resume-cb
.
to avoid adherence to node.js I introduce a small AsyncReader
protocol for the input stream passed to the suspension request.
I wonder if a "user" would ever work with upgradeable repl vs front-end dev tools developers. But I wouldn't know. Sounds cool this contract of suspension request, going to check it out.
I’m not sure how to interpret this.
If you meant “if the contract is cumbersome it doesn’t matter” then why use a more complex contract?
If you meant “upgrading inline has no benefit to users we can just add a flag” (flag which already exists, it’s -n
)” then I disagree, the whole point is to allow users to use fancy or various frontends without having to change the way they start a repl or an app.
Not how I ment it, the callback function that is to be passed to a repl, is only set once and that callback function is written by front-end devtools developers. The users would then just start codeing without worrying about the underlying structure. So if for example unrepl has different printer than default repl, then that printer and all boilerplate will be passed to that function? ex. (lumo.repl/upgdrage-repl (fn [line] ..boilerplate with out-writer..)) where upgrade repl starts a loop.
So my question is, would a user ever have to worry about setting two callbacks or give a repl async function?
allow me to take a step back again, if the repl receives expression before the old return value is returned, do we wan't to add it into a queue, or print the return value asynchronously, possibly in wrong order (if that's even possible, I doubt actually)
In both case its’ a 1-fn API which takes the same callback as argument. I prefer suspension-request because (being value-based) it has clearer semantics and less room for error or abuse.
ok, I guess the suspension-request method would require a little tweak on the current execute-text function?
Maybe the best thing, is to do a PR and then it would probably spark a conversation on github and we could try it out etc. I think there are many that would like to see this feature.
You are giving me an idea but I don’t know if it’s fully baked yet and, if it is, if it’s unholly
the idea would be to use both approaches: suspension-request
as the api and upgrade-repl!
as the implementation. This would require to let most of the caller chain unchanged at the expense of yet another piece of mutable state...
I was overthinking based on your question "what if the user sets the callback twice? can’t start two loops, so: first wins? last wins? exception?", but js is built on event-queue, so first in first out. Probably we are misunderstanding each other 🙂
> I was overthinking based on your question “what if the user sets the callback twice? can’t start two loops, so: first wins? last wins? exception?“, My pondering was mostly that a side-effectful api open room for undefined behaviors (from the contract point of view) which the user (even it’s a frontend dev) may have a hard time to spot as bugs at first.
I was reading again your messages, didn't find where you mentioned where a mutateable state is needed. I'm therefore neutral, I can see a way of one function api where the old loop is only suspended and new loop is takeing over. My thumbs up ment, you have theory on how to solve it and I'd like to see it implemented and try it out. If someone is really the final call here is antonio.
mutable state: if I don’t want to change the signature of the call-chain to execute-text then I need to set some mutable flag — pretty sure it would incur technical debt
How about adding callback to
(defn- execute-text
[source {:keys [expression? print-nil-result? filename session-id] :as opts}]
result-writer-cb
just thinking out loud like before.
That could be passed into eval part of repl to control the return value logic?As a typical user, when I wanted to take over the repl, I found myself mixing core.async and the module readline https://github.com/hlolli/lumo-quiz-game/blob/master/src/lumo_quiz_game/main.cljs#L75-L78
unless I’m ocnfused about you ran it you didn’t take over the repl, you wrote a term app
I’m not sure how to interpret this.
If you meant “if the contract is cumbersome it doesn’t matter” then why use a more complex contract?
If you meant “upgrading inline has no benefit to users we can just add a flag” (flag which already exists, it’s -n
)” then I disagree, the whole point is to allow users to use fancy or various frontends without having to change the way they start a repl or an app.
@cgrand you can use the development workflow
boot dev
to watch & build the JS + CLJS
yarn dev
to launch it in Node.js without having to build the C++