arraysmatlaboopsyntaxmatlab-class

indexed object dot notation method gives scalar property


I'm seeing an issue when I try and reference an object property after having used a dot notation to apply a method. it only occurs when I try to index the initial object

classdef myclassexample

properties
    data
end    

methods   
    function obj = procData(obj)            
        if numel(obj)>1
            for i = 1:numel(obj)
                obj(i) = obj(i).procData;
            end
            return
        end
        %do some processing
        obj.data = abs(obj.data);
    end
end
end

then assigning the following

A = myclassexample;
A(1).data= - -1;
A(2).data =  -2;

when calling the whole array and collecting the property data it works fine

[A.procData.data]

if i try and index A then i only get a scalar out

[A([1 2]).procData.data]

even though it seems to do fine without the property call

B  = A([1 2]).procData;
[B.data]

any ideas?


Solution

  • I would definitely call this a bug in the parser; A bug because it did not throw an error to begin with, and instead allowed you to write: obj.method.prop in the first place!

    The fact that MATLAB crashed in some variations of this syntax is a serious bug, and should definitely be reported to MathWorks.

    Now the general rule in MATLAB is that you should not "index into a result" directly. Instead, you should first save the result into a variable, and then index into that variable.

    This fact is clear if you use the form func(obj) rather than obj.func() to invoke member methods for objects (dot-notation vs. function notation):

    >> A = MyClass;
    >> A.procData.data       % or A.procData().data
    ans =
         []
    >> procData(A).data
    Undefined variable "procData" or class "procData". 
    

    Instead, as you noted, you should use:

    >> B = procData(A):    % or: B = A.pocData;
    >> [B.data]
    

    FWIW, this is also what happens when working with plain structures and regular functions (as opposed to OOP objects and member functions), as you cannot index into the result of a function call anyway. Example:

    % a function that works on structure scalar/arrays
    function s = procStruct(s)
        if numel(s) > 1
            for i=1:numel(s)
                s(i) = procStruct(s(i));
            end
        else
            s.data = abs(s.data);
        end
    end
    

    Then all the following calls will throw errors (as they should):

    % 1x2 struct array
    >> s = struct('data',{1 -2});
    
    >> procStruct(s).data
    Undefined variable "procStruct" or class "procStruct". 
    
    >> procStruct(s([1 2])).data
    Undefined variable "procStruct" or class "procStruct". 
    
    >> feval('procStruct',s).data
    Undefined variable "feval" or class "feval". 
    
    >> f=@procStruct; f(s([1 2])).data
    Improper index matrix reference. 
    

    You might be asking yourself why they decided to not allow such syntax. Well it turns out there is a good reason why MATLAB does not allow indexing into a function call (without having to introduce a temporary variable that is), be it dot-indexing or subscript-indexing.

    Take the following function for example:

    function x = f(n)
        if nargin == 0, n=3; end
        x = magic(n);
    end
    

    If we allowed indexing into a function call, then there would be an ambiguity in how to interpret the following call f(4):

    This confusion is caused by several things in the MATLAB syntax:

    Another point of confusion is comma-separated lists and functions that return multiple outputs. Example:

    >> [mx,idx] = max(magic(3))
    mx =
         8     9     7
    idx =
         1     3     2
    
    >> [mx,idx] = max(magic(3))(4)     % now what?
    

    Should we return the 4th element of each output variables from MAX, or 4th element from only the first output argument along with the full second output? What about when the function returns outputs of different sizes?

    All of this still applies to the other types of indexing: f()(3)/f(3), f().x/f.x, f(){3}/f{3}.

    Because of this, MathWorks decided avoid all the above confusion and simply not allow directly indexing into results. Unfortunately they limited the syntax in the process. Octave for example has no such restriction (you can write magic(4)(1,2)), but then again the new OOP system is still in the process of being developed, so I don't know how Octave deals with such cases.


    For those interested, this reminds me of another similar bug with regards to packages and classes and directly indexing to get a property. The results were different whether you called it from the command prompt, from a script, or from a M-file function...