Isargout vs. feval

Using a ~ character in the output list when calling a function causes the output value to be ignored.

This syntax and behavior is Matlab compatible, but AFAIK, Matlab only skips saving the value in the current workspace and provides no way for the function to know whether the output will be ignored, so there is no (easy) way to avoid computation of the value. It’s all about saving memory in the workspace of the caller.

In Octave, we have a function, isargout that allows a function to check whether an output value will be ignored so that computation can also be avoided. It works for simple function calls. For example,

function varargout = foofun ()
  varargout = cell (1, nargout);
  printf ("ignored outputs:");
  printf (" %d", find (! arrayfun (@isargout, 1:nargout)));
  printf ("\n");
endfunction
[a, ~, b, ~] = foofun ();

will display

ignored outputs: 2 4

It also works when calling a function using a function handle or through feval, so

fh = @foofun;
[a, ~, b, ~] = fh ()
[a, ~, b, ~] = feval (fh)
[a, ~, b, ~] = feval ("foofun")

will all display the same output as above.

This feature works for feval because we don’t do anything to invalidate the list of ignored outputs in the assignment expression when calling the internal interpreter::feval function. That’s great when you want to implement a function like feval but not if you just want to use interpreter::feval in your .oct file to call an external function without inheriting the caller’s list of ignored output parameters.

I just “fixed” the mexCallMATLAB function (see https://savannah.gnu.org/bugs/index.php?59597) so that it doesn’t propagate the prevailing isargout info to the function it calls.

But the problem remains for anyone using Octave’s interpreter::feval functions directly. Forcing everyone who uses interpreter::feval to make this choice seems like trouble. I believe builtin (which ultimately calls interpreter::feval) would be affected similarly. Are there others?

Maybe the best fix is to add a parameter to interpreter::feval to specify that the prevailing isargout info should be preserved and otherwise disable it by default?

Comments, questions, and suggestions would be helpful.

Thanks.

A grep for isargout in the scripts/ directory shows that we are using the construct 28 times. This isn’t so many instances, but I still think it is a useful feature that we might want to exploit more in the future. Hence, I think it is worth preserving.

We have two different behaviors that the code should exhibit. Obvious solutions are either to segregate by name and have two different functions, or segregate by a flag passed to just one function.

We could make this a topic for discussion as well, but it is my belief today that we don’t want to require existing external code to be re-written (either to use a new function or pass a new flag). That design choice means core Octave has to take on the pain of modifying our code. I’m not saying it can’t be automated with perl or some such solution, but finding all instances of feval and inserting a boolean true in to the parameter list is more difficult than a simple search&replace to change one function name to another name (feval -> feval_internal). Working against this is the idea that if two pieces of code do nearly the same thing they should be joined together for maintenance ease. In the future if we find a different problem with feval it will need to be fixed in two places.

One last suggestion, is it possible to expose a different prototype of the function to .oct-file writers than core Octave users? If that were the case, one could an internal prototype with default argument set one way versus a header file used for .oct-file inclusion where it is set the other way.

feval (bool do_isargout = true);   # internal header file

feval (bool do_isargout = false);   # external header file like mex.h

There are only a few places where we really need to preserve the info to make isargout work. Currently it will be preserved in some cases where feval is called in .oct files. But I doubt that is what users actually want most of the time. Unless you are trying to write a function that would behave like Octave’s feval does, then you wouldn’t want the isargout info to be preserved. I suspect that this potential problem mostly goes undetected. I also don’t think we want to expose different defaults for feval depending on whether it is used externally/internally. There is no feval function in the mex interface, just mexCallMATLAB, and since Matlab doesn’t have isargout, then it is safe to never pass the info required for isargout through that interface.

I’ll look at adding an optional parameter for feval that we can use internally in Ffeval, Fbuiltin, and the evaluation of function handles.