jupyter-xeus / xeus-octave

Jupyter kernel for GNU Octave
https://xeus-octave.readthedocs.io/
GNU General Public License v3.0
57 stars 10 forks source link

Use output when possible #50

Open AntoinePrv opened 1 year ago

AntoinePrv commented 1 year ago

Executing 1+1 does not output but only prints to stdout.

jupyter_kernel_test activation

code_execute_result = [
    {"code": "6*7", "result": "42"},
    {"code": "cos(pi)", "result": "-1"},
]
rapgenic commented 1 year ago

I don't really understand what is the problem here, executing 1+1 in a cell produces the expression ans = 2 which does not end in a semicolon, so is correctly displayed...

See this screenshot of the kernel spy:

Schermata del 2023-01-10 13-44-22

AntoinePrv commented 1 year ago

This may be something that is different between Octave and Python.

Notice the [2]: 2 in the Python output? This is because in Pyhon 1+1 does not print anything, it's IPython that decide to print the last value in a different channel from what Python would print (in print(1+1)).

Similarily, when doing a = 2, Python does not print anything, but Octave does.

I wonder if we can get Octave to run in "script" mode, where it would print only explicit calls to stdout/stderr, and control the cell output from xoctave. Or perhaps that is not what user except?

Python

Screen Shot 2023-01-10 at 13 58 22

Octave

Screen Shot 2023-01-10 at 13 59 43

rapgenic commented 1 year ago

Ok thank you, I understand now.

So all this is happening because when the interpreter executes a line without the semicolon it will always call display (indipendently of xoctave), which in this case is defined inside display.m and calls display_data.

If you wanted something like ipython, you would need to prevent octave from calling display either by adding manually the semicolons or automatically by editing the code before execution (or by changing the parse tree) or by clearing the display function. Then after the execution the xinterpreter should check if the ans symbol is defined and send the execute_result response (I think you're referring to that, right?). I don't know about a "script mode" (it would probably be called silent mode in octave), it might exist actually.

However in my opinion this is far from what the Octave user expects, as we're used to being able to show variables just by removing the semicolon. I think if this behaved as ipython, any experienced octave user would think that it's not working.

For me, for example, this is a major point, and one of the reasons I still won't use ipython: I often need to see what the content of a variable is and it would be cumbersome to write a printf call any time I need to do that.

rapgenic commented 1 year ago

If you really think this is important, maybe it could be a configurable behaviour, but I think we should be keeping the current one at least as a possibility (if not the default).

AntoinePrv commented 1 year ago

execute_result response (I think you're referring to that, right?

Yes.

However in my opinion this is far from what the Octave user expects, as we're used to being able to show variables just by removing the semicolon. I think if this behaved as ipython, any experienced octave user would think that it's not working.

I also think we should stick to Octave behavior as much as possible.

I wonder then if we should ever send something in the execute_result response. For instance should xoctave print anything if there is a; at the end of a cell? If yes then would should print a at the end of cell, octave or xoctave?

Another thing with Ipython (and the execute_result response) is that it set a variable _2 (or whichever the count is) with that value. A bit like ans but more general.

rapgenic commented 1 year ago

I wonder then if we should ever send something in the execute_result response.

At the moment no execute_result is sent. In the past I purposefully chose not to use it because I couldn't display more than one output and so display_data became my primary choice.

In my opinion we could keep this behaviour, as inside JupyterLab there is no visual difference (besides the little [1] to the left of the output) between the two methods, unless such answer is needed in other cases (e.g. if the kernel were used to do some kind of scripting via the jupyter protocol) and the single result per cell actually made more sense.

If this were the case, maybe it could be made optional, and the result could be sent anyway independently of the semicolon, whenever the ans symbol has been set after a cell execution. We would then possibly have two identical outputs, like this (but it would be after the user decided to do so):

In [0]: enable_execute_result
In [1]: 2 + 2
ans = 4          <= this would be from the display call
Out [1]: 4       <= this would be from the execute_result message

Another thing with Ipython (and the execute_result response) is that it set a variable _2 (or whichever the count is) with that value. A bit like ans but more general.

Totally possible and I quite like the idea. It would be enough to assign to a new variable the ans value after every cell execution

rapgenic commented 1 year ago

I've been doing some research on this, related to the _jupyter_repr_ proposal.

The idea would be that _jupyter_repr_ would be called for the last statement in each cell, as it is done in ipython (in addition to native octave display called for each statement not ending in semicolon).

As a reference, octave displays an output value when a statement without semicolon is:

In addition it will also assign and print the ans variable when an expression has a return value, to which we don't have access (unless we execute the parse tree manually)

This has turned out to be quite difficult and I have not found a decent solution yet.

The main reason is that I haven't found a way to reliably decide if a statement is a function or a variable and if it returns a value or not (the two things buried in the octave code). For example the expressions:

Have the same parse tree:

graph TD;
  statement --> expression
  expression --> identifier

Yet one has a meaningful value, the other one shall not return anything, in fact the following expressions are and behave differently:

This is what I tried: