JonyEpsilon / gorilla-repl

A rich REPL for Clojure in the notebook style.
http://gorilla-repl.org
MIT License
887 stars 104 forks source link

Segments with multiple outputs only save last one #170

Open JonyEpsilon opened 10 years ago

JonyEpsilon commented 9 years ago

Note, as this will likely involve a change to the file-format, should coordinate with #192 and #193 which both depend on persistent segment metadata.

dtolpin commented 9 years ago

I actually think that only the last output should consistently shown, saved, and restored - a segment is an implicit 'do' block, it should only have one 'result'. Showing all outputs means that a segment having just a bunch of top-level definitions will have output full of useless nils. To display "multiple outputs", one can put them into a data structure.

JonyEpsilon commented 9 years ago

The idea here (although it was buggy at first, and still isn't quite right) is to follow what the text REPL does, by evaluating each form in an input separately. [On a deeper level, this is reflected in nRPEPL which returns a separate response message for each top-level form.] This corresponds to my intuition about what a REPL should do, although that might be a statement about my particular background more than REPLs in general (Mathematica and Racket both have this behaviour).

I don't think I see a good reason to change the current behaviour - as it's what I and I would imagine others expect and use. Regarding the idea of a segment being a do block with a single result: I'm not sure I see it that way, not least because there's no way to assign the result. [Which is not to say that wouldn't be interesting - I know Session is experimenting with the idea of the results from evaluation being available to the code.]

dtolpin commented 9 years ago

The current behaviour is not something one can 'use', because one cannot save the results. You need to re-run the worksheet to get these results transiently, and you lose them every time you save and reload.

nREPL returns values for all top-level forms, but this is for bandwidth optimization rather than for any semantic purposes. You can as well further optimize the bandwidth by sending all of the segments at once and then spreading the results throughout the worksheet. In a normal REPL (for example, SLIME, or any terminal-based REPL for any flavour of Lisp, or ML, or Haskel) you cannot enter more than a single top-level form without executing it. how would you do this in anything but web notebook?

Just to make it clear, when you type several forms on the same line in DrRacket, racket reads the first form, evaluates it, and sends back the result, then reads the second form. The reason that you need to press Enter is not because Enter 'triggers' execution, but just because the input is line buffered, and no data is ever sent to Racket before you press Enter. nREPL is different. For the optimization sake you can send multiple forms for execution and get all of the results together. That does not mean you should show them this way. REPL is by definition read-evaluate-print-loop where read means single top-level form. That's how read works in Clojure, by the way. One cannot read more than a single form with a single read.

The patch I proposed just makes the behavior consistent with both the current implementation of Gorilla REPL (which cannot save multiple values), and with definition of REPL in Lisp family of languages.

JonyEpsilon commented 9 years ago

There's an interesting discussion over on the clojure.dev mailing list that you might have seen:

https://groups.google.com/forum/#!topic/clojure-dev/Dl3Stw5iRVA

It relates to a new stream-based socket REPL facility that will be built in to an upcoming Clojure release. There's some discussion about the expectation from a REPL, and on the one side it's argued that it really is a character-based 'streaming' interaction. A good fraction of people, though, seem to think of it as a more reply/response based interaction, I suspect because that is the most common use case. I think I'd agree that the stream-based approach is strictly more powerful, and is what has traditionally been understood as a REPL in LISPs. Gorilla REPL definitely isn't that though! I don't think it makes much sense, in the notebook environment, to support arbitrary interaction - so I wouldn't expect to see a text adventure written in Gorilla any time soon :-) It definitely is more of a reply/response interaction mode.

Anyway, that's slightly off-topic, but I thought you might be interested nonetheless. Back to the point ... I would say that the current behaviour does get used. A typical example would be:

screen shot 2015-05-17 at 17 34 32

where I want to run a few related forms at once to inspect the results of a previous computation. I'm reluctant to change existing behaviour (especially behaviour that I use ;-)) without a compelling reason, so maybe it's right to step back and understand what problem it is that your patch fixes. You mentioned something about generating unwanted nils. Is this the root problem? I tend not to see that, as most of my top-level forms either return a value I'm interested in, or else are defs or defns that return the var. Perhaps you could elaborate on what it is that causes problems for you?

dtolpin commented 9 years ago

My patch makes a saved and reloaded segment consistent with a just computed segment. Currently, only the last value per segment is restored if a worksheet is saved. Intermediate values appear only in HTML tree, but not in any persistent form. My patch replaces rather than appends values to the DOM, unifying the behaviour.

To run and compare multiple forms at once one can build a data structure out of the forms, that also makes the output better organized and readable. In your example, the code would be:

 {:node-count (tree/count-nodes rr)
  :lagrange-score-mmd (lagrangian/lagrange-score-mmd
                                       (lagrangian/prepare-lagrange-mmd-data dat t-step '[theta])
                                       t-step '[theta] '[omega] rr))
 :render (render/mathematician-view rr)
 :fullform (mma/fullform rr)
 :rr rr}

and the output will be easier to read and match (and analyse programmatically).

Any behaviour is good as long as it is consistent. The current behaviour is not --- I as a user prepare a nice worksheet, store it, only to discover when I reload that many of my nice results, some of which took quite some time to compute, are gone. Gorilla REPL of course can be fixed to store multiple outputs, however, looking at the code, this requires a change in the format and moving many things around. Hence I thought that a simple fix that made the behaviour consistent (and less frustrating) would be desirable.

JonyEpsilon commented 9 years ago

I certainly agree that the current behaviour is broken, hence this issue :-) But I think the better solution is to fix the save/load of multiple outputs.

keorn commented 9 years ago

I would also like to add that I am used to the IPython worksheets (now jupyter.org), which are very popular, where each cell returns the final value (similar to do block).