bugGNU Octave - Bugs: bug #50359, clearer documentation for...

 
 

bug #50359: clearer documentation for difference between isnull and isempty

Submitter:  Ernst Reissner <ernstreissner>
Submitted:  Mon 20 Feb 2017 10:12:37 AM UTC
   
 
Category:  Documentation Severity:  2 - Minor
Priority:  3 - Low Item Group:  Documentation
Status:  Fixed Assigned to:  None
Originator Name:  Ernst Reissner Open/Closed:  * Closed
Release:  * dev Operating System:  * Any
Fixed Release:  None Planned Release:  None
* Mandatory Fields

Add a New Comment Rich Markup
   

Jump to the original submission

Tue 24 Oct 2017 02:45:55 PM UTC, comment #26: 

Thank you Rik, I find your explanation pretty clear (maybe I am a bit biased, because I followed this bug report).

Kai Torben Ohlhus <siko1056>
Group Member
Sat 21 Oct 2017 09:36:42 PM UTC, comment #25: 

I clarified the documentation for isnull in this cset (http://hg.savannah.gnu.org/hgweb/octave/rev/48cf0f4cc7c8).  Closing report.

Rik <rik5>
Group administrator
Thu 14 Sep 2017 10:23:15 PM UTC, comment #24: 

@Mike: Thank you for the example.  So containers.Map gets refactored regarding this?  Currently isnull is not used there, so I was not able to find this example for making it plausible myself.

The documentation is good, only the example can be misleading without knowing this bug report.


     Return true if X is a special null matrix, string, or single quoted
     string.

     Indexed assignment with such a value on the right-hand side should
     delete array elements.  This function should be used when
     overloading indexed assignment for user-defined classes instead of
     `isempty', to distinguish the cases:

    `A(I) = []'
          This should delete elements if `I' is nonempty.

    `X = []; A(I) = X'
          This should give an error if `I' is nonempty.

     See also: isempty, isindex.


I suggest as example:


    [... as before ...]

    `A(I) = []'

    and

    `X = []; A(I) = X'.

    Assuming in both cases, that `I' is nonempty, the first
    assignment should delete the respective elements,
    whereas the latter one assigns them an empty matrix, e.g.
    `X' is equivalent to `zeros (0, 0)' in this case and
    will probably result in an error.

     See also: isempty, isindex.


Kai Torben Ohlhus <siko1056>
Group Member
Thu 14 Sep 2017 07:25:54 PM UTC, comment #23: 

So it appears that this just isn't a problem for Matlab, because they don't support the delete syntax in an overloaded subsasgn function.

That's fine for them, but Octave aspires to be a superset of Matlab and this seems like a useful, although only occasionally needed, feature.

What needs to change?  Do we need a full example somewhere in the object oriented programming section where we show the difference between isnull and isempty.  Is it enough to rephrase the docstring for isnull?

Rik <rik5>
Group administrator
Thu 14 Sep 2017 06:55:05 PM UTC, comment #22: 

Kai - for a practical example, it would be nice if something like containers.Map allowed deleting a key-value pair like this


map = containers.Map ({"one", "two"}, {1, 2});
map("two") = [];


This kind of indexing would normally delete the element of map that the index "two" references, but in this case it just assigns it a value of an empty array.

I don't know which behavior is compatible, I'm just giving an example where assigning to a null array can't be done by simply forwarding the assignment to an array that is stored in the object. Not all objects are wrappers around a matrix so the transitive assignment would work.

Here is a practical example from the symbolic package:


x = sym ("x", [10, 1]);
x(3) = [];


This does work, but only because it also works with zeros(0,0) or any empty array acting as a signal for deletion.

Mike Miller <mtmiller>
Group Member
Thu 14 Sep 2017 06:27:40 PM UTC, comment #21: 

Output of comment #20 using Matlab R2017a with a small modification `disp(val)` to `disp(varargin)`:


----
     3

    type: '()'
    subs: {':'}

    [3]

   0

----
----
     3

    type: '()'
    subs: {':'}

    {[]}

   0

----
----
     3

    type: '()'
    subs: {':'}

    {[]}

   0

----


Kai Torben Ohlhus <siko1056>
Group Member
Thu 14 Sep 2017 03:48:52 PM UTC, comment #20: 

@Kai: Could you try a variation of your demo class which uses varargin?


classdef aclass
  properties
    a
  end

  methods
    function obj = subsasgn(obj,s,varargin)
      disp('----')
      disp(nargin)
      disp(s)
      disp(val)
      disp(isempty(varargin))
      disp('----')
    end
  end
end


And then as before


clear CLASSES; clear all; clc;

obj = aclass();
obj(:) = 3;
obj(:) = [];
obj(:) = zeros (0,0);



Rik <rik5>
Group administrator
Thu 14 Sep 2017 02:51:30 PM UTC, comment #19: 

Here is a simple Matlab R2017a demo class:


classdef aclass
  properties
    a
  end

  methods
    function obj = subsasgn(obj,s,val)
      disp('----')
      disp(nargin)
      disp(s)
      disp(val)
      disp(isempty(val))
      disp('----')
    end
  end
end


nargin seems to be no Matlab compatible way to figure out if a delete assignment happened:


clear CLASSES; clear all; clc;

obj = aclass();
obj(:) = 3;
obj(:) = [];
obj(:) = zeros (0,0);
----
     3

    type: '()'
    subs: {':'}

     3

   0

----
----
     3

    type: '()'
    subs: {':'}

   1

----
----
     3

    type: '()'
    subs: {':'}

   1

----


I think Octave's `isnull` provides much more (in the negative sense) than there is possible with Matlab and can be made an internal function.

When I have an object of a class with array data, a call like


my_class(1:2) = [];


usually forwards the assigned whatsoever (null or zeros(0,0)) to my underlying data where the delete operation gets detected and applied, as answer to comment #13 maybe.  Why should I write code like:


function obj = subsasgn(obj,s,val)
  if (isnull (val)) ## User wants to delete
    mydata = [];    ## Forward delete
  endif
endfunction


That is why I asked in comment #14 for a practical example, at least Octave itself does not have any and I have not seen any code where this might be important to know?

Coming back to the roots of this report.  isnull is a pure Octave   solution for user defined classes, not applicable for Java's null.  As long as no expert tells us, how this can be done in Matlab, we cannot make any progress here.

Kai Torben Ohlhus <siko1056>
Group Member
Fri 08 Sep 2017 02:37:16 PM UTC, comment #18: 

who has matlab???????
This must be a common problem...
Maybe themathworks sponsors octave a license. ... :O)

Ernst Reissner <ernstreissner>
Thu 07 Sep 2017 03:44:24 PM UTC, comment #17: 

Yes, if there were another way of distinguishing NULL from EMPTY then we could drop this function.

Rik <rik5>
Group administrator
Thu 07 Sep 2017 07:53:22 AM UTC, comment #16: 

@rik Great idea!!
But we need someone with Matlab at hand to check,
whether it is true what you suspect.
I would do instantly but have no Matlab.

If you are right, we could drop isnull altogether, right?
Or internalize as _isnul_ as Kai suggested...

Ernst Reissner <ernstreissner>
Tue 05 Sep 2017 08:49:34 PM UTC, comment #15: 

Here is a thread on Matlab Central about how Matlab deals with null assignment (https://www.mathworks.com/matlabcentral/newsreader/view_thread/310020).

The quick answer is that


x(idx) = []


is flagged by the parser and the right-handside means NULL or empty and this triggers a delete action.

When you do the same assignment without indexing


x = [];


The parser, instead, decides that you want an empty matrix so this is equivalent to


x = zeros (0, 0);


When you code


rhs = [];
x(idx) = rhs;


the parser knows that the right-handside is not the special pattern "[]", but a variable so it doesn't trigger any delete action.

The prototype for subsasgn is


subsasgn (VAL, IDX, RHS)


and is equivalent to


VAL(IDX) = RHS


Now consider the case of an object which has overloaded the subsasgn function.  How can the coder know whether the user has typed


object(IDX) = [];


versus


object(IDX) = zeros (0,0);


In either case, the coder will be delivered a variable RHS.  This variable will be empty, so isempty() can not distinguish what was intended.  Instead, if you want to discover whether a delete operation was requested you need to use isnull().  For example, maybe a particular idx is read-only.  Then you might write something like


if (isnull (RHS))
  if (idx == 3)
    error ("object 3 is read-only");
  endif
endif


The Matlab documentation for subsasgn is here: http://www.mathworks.com/help/matlab/ref/subsasgn.html.  Conveniently for them, they do not address this case and how you might distinguish between deletion and assignment.

I have a thought here, though, and it might be worth investigating.  If the user writes the subsasgn function prototype as


function retval = subsasgn (VAL, IDX, varargin)


then it may be the case that the Matlab parser does not assign a value to varargin for the NULL case.  If that is true then one could write


if (nargin < 3)
  % Delete operation
else
  % Assignment operation
end



Rik <rik5>
Group administrator
Sun 05 Mar 2017 05:47:27 PM UTC, comment #14: 

@Mike: Can you point me to a small example, where isnull for subsasgn is used (comment #12)?

Kai Torben Ohlhus <siko1056>
Group Member
Fri 03 Mar 2017 05:09:26 PM UTC, comment #13: 

I guess a question raised by my claim, if there is no isnull function in Matlab, how would one write a subasgn method in Matlab that supports deleting elements?

Mike Miller <mtmiller>
Group Member
Fri 03 Mar 2017 05:06:07 PM UTC, comment #12: 

No, I strongly disagree with renaming the function to `__isnull__`, because it is not for internal use only.

The point of the function is for user classes implementing a subsasgn method in their class, for example. A class that implements its own indexing must be able to differentiate between


myobj(3) = [];


and


x = [];
myobj(3) = x;


and the only way to do so is with the isnull function.

I support adding more text to the doc string, and maybe a small code example showing how it might be useful in a function. But renaming it with underscores gives the impression that it should not be used by Octave users, and that is not true. In fact it is primarily useful for Octave users, just in a very specific use case.

Mike Miller <mtmiller>
Group Member
Fri 03 Mar 2017 12:11:36 PM UTC, comment #11: 

Another suggestion is to convert `isnull` to `__isnull__`, a internal undocumented function.

1.) `isnull` is not MATLAB compatible.
2.) `isnull` is only used in "libinterp/octave-value/ov-null-mat.cc" and "libinterp/parse-tree/pt-mat.cc" for internal purpose in tests.
3.) `isnull` gets users the wrong idea of it's specific purpose and is irrelevant for most user code applications.

Any contrary opinions? If not, I'll provide a cset for dev next week.

Kai Torben Ohlhus <siko1056>
Group Member
Thu 23 Feb 2017 10:45:07 PM UTC, comment #10: 

After all, I understood. It is no bug and no incorrect result.
If the documentation is for beginners also,
I think it would help to give an explanation like that.

Ernst Reissner <ernstreissner>
Thu 23 Feb 2017 07:01:17 PM UTC, comment #9: 

Ernst - the only reason for this difference to exist is because it must for Matlab compatibility.

Matlab strictly differentiates between the two assignments jwe shows in comment #7, so Octave must as well. This is the entire reason for the existence of the null type and why it loses its type upon assignment to a variable. Once a null type is assigned, it is no longer null. The only utility of the null type is for this assignment syntax that allows elements of an array to be deleted.

Thus the only use of the isnull function is probably in an overloaded class implementation of the subsasgn method.

Mike Miller <mtmiller>
Group Member
Thu 23 Feb 2017 02:35:11 PM UTC, comment #8: 

I am not so familiar with octave as John is,
thus the difference seems crazy to me:
assignment seems to change the type of an expression.

This is what Mike says and I feel,
for a novice like me, this note would help.

Still I cannot figure out, why this is useful or makes sense?
The explanation in the help string: i do not understand.

Ernst Reissner <ernstreissner>
Thu 23 Feb 2017 02:21:13 PM UTC, comment #7: 

The whole reason this special type was added to Octave was to handle the (crazy?) difference between


x(idx) = [];


and


t = [];
x(idx) = t;


This seems reasonably clear to me from the example in the docstring for isnull.

John W. Eaton <jwe>
Group administrator
Wed 22 Feb 2017 01:32:16 AM UTC, comment #6: 

Right, the summary sentence tries to capture that

> Return true if X is a special null matrix, string, or single quoted string.


Some text could be added to clarify that a special null type is null only when used as a literal. Once it is assigned to a variable it loses its null property.

Mike Miller <mtmiller>
Group Member
Tue 21 Feb 2017 12:35:55 PM UTC, comment #5: 

Now I understand.
But with the text in the documentation:
no chance for me, I must admit.

Ernst Reissner <ernstreissner>
Tue 21 Feb 2017 11:37:50 AM UTC, comment #4: 

As far as I understand this function, it is checking for a singleton "null_XXX" objects with unique reference (therefore non-assignable):


>> typeinfo ([])
ans = null_matrix
>> x = []
x = [](0x0)
>> typeinfo (x)
ans = matrix
>> typeinfo ("")
ans = null_string
>> typeinfo ('')
ans = null_sq_string


Kai Torben Ohlhus <siko1056>
Group Member
Tue 21 Feb 2017 10:07:51 AM UTC, comment #3: 

Apologies, it is still not clear to me:
if

%!assert (isnull ([]), true)
%! x = [];
%! assert (isnull (x), false);

then assignment of [] to x or reading [] from x
changes the object.
Is this true?

Also from the documentation,
i cannot figure out,
what is meant by the difference between isnull and isempty.
isnull seems to imply isempty but not the other way round,
correct?



Ernst Reissner <ernstreissner>
Mon 20 Feb 2017 10:10:27 PM UTC, comment #2: 

I think this is just a misunderstanding of what is meant by the help for the function isnull. It seems pretty clear to me, but maybe some small clarifications could be added to make it more explicit. I agree that the docs, tests, and examples are correct as stated.

Mike Miller <mtmiller>
Group Member
Mon 20 Feb 2017 05:39:23 PM UTC, comment #1: 

What do you mean by "In the documentation, Section 4.3"? I don't see anything about deletions in "4.3 Single Precision Data Types".

https://www.gnu.org/software/octave/doc/v4.2.0/Single-Precision-Data-Types.html#Single-Precision-Data-Types

What you are referring to is Section "3.3 Object Sizes"

https://www.gnu.org/software/octave/doc/v4.2.0/Object-Sizes.html#XREFisnull

Anyway, I can confirm the example:


>> b=javaArray("java.math.BigDecimal",3)
b =

<Java object: java.math.BigDecimal[]>

>> b(3)=javaObject("java.math.BigDecimal",3.0)
b =

<Java object: java.math.BigDecimal[]>

>> isnull(b(2)) % yields ans = 0
ans = 0
>> isempty(b(2)) % yields ans = 1
ans = 1
>> b(2)
ans = [](0x0)


So why do you expect your example to be true? Can you describe, what you want to check?

From the test cases of "isnull" I see:


%!assert (isnull ([]), true)
%!assert (isnull (zeros (0,3)), false)
%!assert (isnull (""), true)
%!assert (isnull (''), true)
%!test
%! x = [];
%! assert (isnull (x), false);


And some own tries yield:


>> isnull (zeros (0,0))
ans = 0
>> isempty (zeros (0,0))
ans = 1
>> zeros (0,0)
ans = [](0x0)


Actually, this function is not Matlab compatible and I don't really know its purpose. So I don't recommend to use it.

Kai Torben Ohlhus <siko1056>
Group Member
Mon 20 Feb 2017 10:12:37 AM UTC, original submission:  


In the documentation, Section 4.3 something strange is written on isnull
A(I) = [] This should delete elements if I is nonempty.
X = []; A(I) = X
This should give an error if I is nonempty.

This cannot be.

Also I had a look:
    b=javaArray("java.math.BigDecimal",3)
    b(3)=javaObject("java.math.BigDecimal",3.0)

isnull(b(2))  % yields ans = 0
isempty(b(2)) % yields ans = 1

The first is not correct.

Ernst Reissner <ernstreissner>

 

(Note: upload size limit is set to 16384 kB, after insertion of the required escape characters.)

Attach Files:
   
   
Comment:
   

No files currently attached

 

Depends on the following items: None found

Items that depend on this one: None found

 

Carbon-Copy List
  • -email is unavailable- added by rik5 (Posted a comment)
  • -email is unavailable- added by jwe (Posted a comment)
  • -email is unavailable- added by siko1056 (Posted a comment)
  • -email is unavailable- added by ernstreissner (Submitted the item)
  •  

    There are 0 votes so far. Votes easily highlight which items people would like to see resolved in priority, independently of the priority of the item set by tracker managers.

    Only group members can vote.

     

    Follow 15 latest changes.

    Date Changed by Updated Field Previous Value => Replaced by
    2019-02-22 mtmiller Carbon-CopyRemoved 80942 -
    2017-10-22 ernstreissner Carbon-CopyRemoved -email is unavailable- -
    2017-10-21 rik5 StatusNeed Info Fixed
        Open/ClosedOpen Closed
    2017-03-05 siko1056 Assigned tosiko1056 None
    2017-03-03 siko1056 Severity3 - Normal 2 - Minor
        Item GroupIncorrect Result Documentation
        Assigned toNone siko1056
        Release4.2.0 dev
    2017-02-21 rik5 Carbon-CopyRemoved 72865 -
    2017-02-21 rik5 CategoryNone Documentation
        Priority5 - Normal 3 - Low
        Summaryisnull false negative clearer documentation for difference between isnull and isempty
    2017-02-20 siko1056 StatusNone Need Info
    2017-02-20 ernstreissner Carbon-Copy- Added -email is unavailable-

    Back to the top

    Powered by Savane 3.13-f8d8.
    Corresponding source code