Pluto.jl: Multiple outputs and inputs per cell

Created on 24 Feb 2020  路  23Comments  路  Source: fonsp/Pluto.jl

A cell input like:

馃崟 = 1
馃 = 2

should not be legal, because it is not a _single expression_. This might be unexpected (and part of the learning curve) for new users, and we could accept it without breaking the paradigm by:

  • Splitting up the expression into two cells.
  • Giving the cell _two outputs_, which are stacked on top of each other with a clear division between them.

A nice hybrid solution is to do the first option, and to design the editor layout in such a way that all cells fluently follow each other. (If code is folded).

Note:
The cell

馃崟 = 1; 馃 = 2

which is syntactic sugar for

begin
    馃崟 = 1
    馃 = 2
end

is a _single_ expression, which is not an assignment, but an evaluation that performs two global assignments. Parsing this cell should be legal (as a single expression), but running it might give an error (depending on what we decide to do with globals).

enhancement expression explorer one day

Most helpful comment

What if you took a page out of the REPL and instead of using different keys for evaluation versus newline, just used enter for both and as soon as the expression is complete, you evaluate? That would make it technically difficult to even enter two different expressions in a single block. And if someone does manage to do this (say but cut-and-paste), then you could just split the cell automatically into two.

All 23 comments

I don't see a reason why this shouldn't be legal from a paradigmatic point of view. I think this should be equivalent to two cells, one doing x=1, and the other y=2 (no emoji keyboard yet, sorry).
It's not only more intuitive for new users, but also convenient, allowing to eg. bulk show/hide some related statements by showing/hiding a code cell.

From a "how do we do this" point of view: I'd have variables rather than cells be the nodes of the dependency graph. Each cell can then execute imperative code; once it's done, the global variables it set are checked for changes, and changes are propagated down the graph.

Internally splitting it up into two cells is a cool solution!
About having the core track variables instead of cells: not all cells
define variables - some are just expressions with output, package imports,
etc. so you would have to track all of them too.

But let's leave this for later?

More general then - we should track expressions.
But yes, makes sense to leave this for later, and for now have 1 expression per cell.

The issue of multiple expression per cell is solved: it is not legal. (To be clear, you _can_ wrap multiple top-level expressions into a single begin ... end or let ... end block.) The question is now how to show a nice error to the user (#39).

We might re-open this one day, by making multiple expressions return multiple outputs.

I'm sure this must have been discussed already, but why not allow multiple lines, and then just do the equivalent of implicitly wrapping in a begin ... end block, instead of having the user do it manually?

This would not require having multiple outputs, and seems to make sense.

Hey!

This is a good question, it comes up often and I have spent some time thinking about it.

Your suggested solution might be implemented some day, but I'd like to keep the current behaviour _for now_. The main reason is:

Reactive notebooks work best if you split your code into multiple cells.

For example, if you have a single cell defining 20 parameters for your model, then _changing one parameter_ will cause the re-evaluation of every cell that (indirectly) depends on _any parameter_. This might be slow, making it less fun to play with your model (disaster!), and it's confusing - you feel like reactivity is not doing what it should do.

Solution

So you should make as many cells as you can! Problem solved? Well not quite, we get the feedback that cells can take up _too much vertical space_, and is more _cumbersome to work with_.
I have implemented a lot of features to make this experience better, and I have more plans, notably this one. Already implemented are:

  • decreased vertical spacing between cells.
  • include the top-level assignee x in the case of x = 123 in the output - Julia only ouputs the value. This way you can hide the code and still see what variable is defined.
  • keyboard shortcuts to jump between cells (PageUp and PageDown).
  • selecting multiple cells to move/fold/delete them all at once.

Two more arguments

Like I mentioned before, it might be confusing that changing

a = 1
b = 2

to

a = 1
b = 345

will also re-run all cells that depend on a. I feel like explicitly writing those into a single block makes this more clear.

Also, I have a more geeky argument:
Julia is a Lisp - each code block has a value. I think this is really charming, and for this reason, a _cell_ in Pluto is a unit: one cell has one expression, returning one object (hence displaying one thing).

But the main thing is: I want UI to motivate you to split your code into cells - it's still WIP but I think it will be nice.

What if you took a page out of the REPL and instead of using different keys for evaluation versus newline, just used enter for both and as soon as the expression is complete, you evaluate? That would make it technically difficult to even enter two different expressions in a single block. And if someone does manage to do this (say but cut-and-paste), then you could just split the cell automatically into two.

I love @StefanKarpinski's idea, what a great way to turn this situation into an opportunity!

I find ctrl+enter shift+enter really frustrating, and a straight up REPL like experience would be amazing!!

Note that this works in Julia but not in Python because in Python you can't tell when an expression ends. Fortunately, Pluto isn't trying to be a frontend for every language, so it doesn't have to worry about this 馃榿

Very nice! I was stuck on a similar idea, but this is realistic and cool.

How about it doesn't run, but just creates and focuses a new cell? (Return to run makes me a bit scared in the REPL - Return is no longer a no-consequence button.) Instead, you have the shortcut Ctrl+S to submit all changes, into a single reactive run (there's also a button for it, but it's still a bit hidden) - so you would ENTER a couple of times to create some cells, and then submit+run them all at once (explicitly).

Also nice would be to select multiple cells and then wrap them together into a let or begin block.

And doing arrow-up/arrow-down should jump between cell borders, like pageup+pagedown right now.

Exciting!

What's the danger of evaluating code? I never have this worry in a normal REPL? Is it that one evaluation might trigger a lot of computation because of the reactive part?

I'd like to try it of course, but I mentioned it because unpredictable (reactive) evaluation is one of the common concerns for Pluto users (#13 #116 #229 #298).

this is really ridiculous to not be able to run multiline codes in a cell without placing them in begin...end. at least have a config that this is possible. it is quiet obvious we group codes in the same cell.

even the generated code with begin...end blocks are not really the default way you write code.

and can the generated code be ordered correctly based on the graph deps? having implicit ordering without the physical ordering is very misleading or confusing.

Maybe this issue should be renamed to be about multiple inputs per cell, as it seems that the discussion has shifted more in this direction.

The suggestions here are interesting and may be worth prototyping/exploring, but to me it seems like it basically boils down to how much credit you want to give the user, and right now I see things leaning towards not giving the user much credit (e.g. "hey user, you're not splitting things into enough cells because you don't understand that hurts reactivity, so we're going to do it for you", or "hey user, you're not going to realize chaging a=1; b=1 to a=1; b=345 is going to re-evaluate cells that depend only on a, so we're going to split them for you").

I would really encourage the opposite, give the user power, just make sure the rules are clear. The solution which corresponds to that to me is clear: implicilty wrap every cell in a being ... end. I've yet to see a _technical_ reason why this is not possible (apologies if I've missed one), its all based on trying to be opinionated. But if the users wants to write,

a = 1
b = 2

in a single cell, why should Pluto make it impossible or at least annoying for them to do so (manually wrapping in begin .. end is annoying, as is clicking the button to do it)? Sometimes lines of code make sense grouped together, and a cell is a perfect visual indication of a grouping. And its straightforward to understand dependent computation is per-cell, so if the user wants to put them in the same cell, I'd say let them.

exactly. why even to have this long thread to explain the importance of grouping logical blocks in a cell.....

@ppalmes Please give us space to talk about this subject in a friendly way - your comments here and on slack are inappropriate.

sorry. i鈥檓 just frustrated because it looks a very good package if the discussion is more open-minded instead of dismissing our point of view. thanks for taking into consideration the main point of discussion.

I would like to reiterate the desire to allow for multi-line input without begin and end. I keep seeing notebooks (eg. many in the computational thinking class) that have cells with a begin block.

Clearly it is convenient, useful and arguably plain old sensical to let people to write more than one line of code in a single cell.

The argument that notebooks might get less fun if they become slow is trumped (IMO) by the fact that it is even less fun to have to write begin and end for every other cell.

Regarding the arguments above:

For example, if you have a single cell defining 20 parameters for your model, then _changing one parameter_ will cause the re-evaluation of every cell that (indirectly) depends on _any parameter_.

This might seem like an issue in theory, except that in practice I would expect parameters defined in a single cell to be logically related such that if another cell depends on one of the parameters, then it probably depends on at least some of the other parameters too. Conversely, I would not expect someone to define a bunch of parameters in a single cell that individually apply to different cells.

will also re-run all cells that depend on a. I feel like explicitly writing those into a single block makes this more clear.

In this example, I would expect the cell that depends on b to also depend on a. If that's not the case, then yes, you probably wanted to define a and b separately (since they are not logically related).

We shouldn't let a hypothetical-worst-case performance issue dictate how we structure code given how important structure is to our reasoning process.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

MikaelSlevinsky picture MikaelSlevinsky  路  7Comments

fonsp picture fonsp  路  4Comments

kapple19 picture kapple19  路  5Comments

MikaelSlevinsky picture MikaelSlevinsky  路  5Comments

briochemc picture briochemc  路  5Comments