Closed mfikes closed 9 years ago
I looked into the behavior Ambly / Piggieback when issuing (print "foo")
in the REPL.
Temporarily revising Ambly to essentilly invoke ((:print opts) "foo")
fails because "foo"
is not the result of an evaluation (and is squelched by Piggieback).
This is cool and raises a couple of questions for me:
print
, followed by flushing *out*
.)Question 2 is only of interest for Ambly because of the Bonjour discovery / configuration UI that it wants to perform during the first part of setup. (See an example of that here). For the little command-line UI that Ambly creates, it involves printing as well as printing without a newline so that it can then do a (read-line)
for the Choice:
prompt.
Other REPLs seem to do a little of what Ambly does with respect to this: They will print a little bit of status (Node.js REPL will indicate which port is involved, the Browser REPL will throw up an indicator that it is waiting for the browser to connect. These are command line UIs that are "preludes" to the REPL going into its loop, but they are simpler than Ambly's because they don't involve prompting (and potential re-prompting if you hit R
for refresh in Ambly), prior to the normal part of the REPL going live.
(I feel like this aspect of Ambly is definitely useful, but at the same time, pushes a little on the boundary of the scope of what REPLs are expected to do.)
It's not just the included node and browser REPLs that use the "system" print
to print without a newline, it's all of them, AFAIK. Same goes for Austin and weasel.
The :print
fn provided to the REPL is (to my understanding) intended to handle values resulting from evaluation, nothing more. This goes back to clojure.main
and its :print
arg (which defaults to prn
so that results of Clojure evaluations are printed readably). @swannodette an I talked about this some here...which I see you commented in as well, but at the bottom.
I think the basic point is that REPL environments always have access to Clojure-side *out*
and *err*
, and they should use them to deliver the results of printing in ClojureScript, and any out-of-band information they need to communicate to a human. Anything printed to those streams will work anywhere, whether you're on a typetype, nREPL, or even using cljs.repl/repl*
in an unforeseen way. cljs.repl
does this already in a bunch of places, including its default error handler: https://github.com/clojure/clojurescript/blob/master/src/clj/cljs/repl.clj#L683 I see no reason why Ambly shouldn't do likewise.
So, I'd suggest that:
:print-no-newline
goes awayprint
, println
, and *out*
and *err*
however is necessary for any output beyond printing the values resulting from evaluation (this constraint is pedantic, insofar as cljs.repl/repl*
already takes care of printing results via :print
).@cemerick Your suggestion is fine with me.
The original motivation for all of this was some 19-Feb-2015 IRC discussion (taken from here):
[12:17:37] dnolen: brucehauman: tjakubow: mfikes: one thing to be aware of the coming word, you cannot just call print/println or flush anymore since you don't know under what context the REPL is being used
[12:17:51] dnolen: opts will always contain the :print and :flush fn that you should use
[12:18:16] dnolen: opts is not consistently passed around however, but you can still get at opts via the *repl-opts* dynamic var
[12:18:25] dnolen: s/word/world
[12:18:44] dnolen: whatever you are doing today will of course will still work
[12:19:08] dnolen: but if nREPL rides on this stuff people will be pretty unhappy if you're printing directly to standard out etc.
[12:19:27] brucehauman: dnolen: thats good to know, and *repl-opts* is helpful
[12:19:28] dnolen: piggieback or whatever
[12:19:44] dnolen: brucehauman: yeah it's a hack, but it's the only way to not break everythign
Subsequent IRC discussion lead to :print-no-newline
.
@swannodette Given the IRC above, do you see any issue with Chas's suggestion in the previous comment (to eliminate :print-no-newline
and have REPLs freely use print functions outside of the case of result printing, which must go through :print
)?
Thank you for that context. To be clear, nREPL has no problem with *out*
and *err*
. Those are captured via a regular dynamic binding, with content passed along to the connected session. What nREPL does differently is it keeps stdout/err separate from communicating the results of evaluation. This makes it easy to programmatically interact with a remote evaluation context w/o trying to ignore print
ed content, apply special presentation transformations to evaluation results, etc.
Given the ClojureScript change I've done the same for Ambly.
@tomjakubowski @bhauman You may be interested in this thread.
@mfikes since you're not using :print-no-newline
anymore (and you were the only user of it?), I'll close this now. If the motivating use case is gone now, presumably that option will eventually disappear from cljs.repl/repl
.
If anyone has any clarifying questions re: nREPL and its handling of printed output vs. evaluation results (or anything else, really), I'd suggest posting to the clojure-tools list, just so the discussions are in a more obvious location. :-)
Per https://github.com/clojure/clojurescript/wiki/Custom-REPLs#output
Note that
:print-no-newline
was requested by me (and in fact, I only just now retroactively added the interface spec above to attempt to have a place where it is documented).This being relatively new, it can perhaps be eliminated if
:print
is revised to have semantics like a single-argumentprint
, or if there is some better solution to the larger issue:The reason I had asked for
:print-no-newline
was to properly support the distinction between when a REPL user issueswhich should produce
and
which should produce
Note that the browser REPL dispenses with this problem by currently simply calling
print
on its own: https://github.com/clojure/clojurescript/blob/master/src/clj/cljs/repl/browser.clj#L150(In other words, if it attempted to follow the contract and call
:print
it would encounter the same problem I did.)