9

It is occasionally convenient to use a function as a "constant" variable of sorts in MATLAB. But when I was using this feature recently, I ran into an unexpected error. When I run the MWE below, I get the error Undefined function or variable 'a'. despite the function being clearly available in the same file. When I comment out the if statement, the error goes away. This seems to imply that MATLAB is pre-interpreting a as a variable even though the variable assignment line is never reached, ignoring the fact that there is a function by the same name. Is this a MATLAB bug or is it somehow the desired behavior?

Here is the MWE:

function matlabBugTest(  )
    if false
        a = 'foo';
    end
    a
end
function b = a()
    b = 'bar';
end

Follow-up:

I know it seems weird to intentionally use the same name for a variable and a function, so I'll give an example of where this can be useful. For instance, you may want to use a function to store some constant (like a file path), but also want to be able to use a different value in case the function cannot be found. Such a case might look like:

if ~exist('pathConstant.m', 'file')
    pathConstant = 'C:\some\path';
end
load(fullfile(pathConstant, 'filename.ext'));

I know that language design decisions are often difficult and complicated, but one of the more unfortunate consequences of MATLAB's choice here to ignore the function by the same name is that it breaks compatibility between functions and scripts/command line. For instance, the following runs without issue in a script:

if false
    a = 'foo';
end
a

where the function a (shown above) is saved in its own file.

tarheels
  • 249
  • 2
  • 6
  • Here is a detailed write-up describing the issue that you are having: http://stackoverflow.com/a/36289118/670206 – Suever May 27 '16 at 00:03
  • Interestingly though, things like package imports *do* respect the conditional if it's super clear how it will result: http://stackoverflow.com/a/37390979/670206 – Suever May 27 '16 at 00:06
  • 1
    Interesting. I would call it a bug, but doing so I would risk getting the collective pride of Matlab against me. Since the code clearly does never reach the branch where the variable is used this problem should never occur in my opinion. Problems with the static code analysis? I would not say it to be worth risking slower execution for though. You may even want to express your opinions to mathworks directly. – patrik May 27 '16 at 06:59
  • @patrik Not to be too prideful, but I don't think the behavior itself is in error since it is more of a language design choice. I do think that the lack of a useful error message in the R2016a opposed to my R2015a versions is an error, however. – TroyHaskin May 27 '16 at 21:16
  • @TroyHaskin Thinking about it a bit more I actually see it could cause troubles to have a variable and a function with same name in that same function. Since Matlab does not implement scope resolution for if statements or loops it could cause troubles `if true, a = 'foo'; end, a. – patrik May 30 '16 at 06:42

2 Answers2

11

It has to do with how Matlab performs name-binding at compilation time. Because matlabBugTest has a line that assigns a value to a, a is determined to be a variable, and the later line with a is a reference to that variable and not a call to the local function. More modern versions of Matlab, like my R2015a install, gives a more clear error message:

At compilation, "a" was determined to be a variable and this variable is uninitialized. "a" is also a function name and previous versions of MATLAB would have called the function. However, MATLAB 7 forbids the use of the same name in the same context as both a function and a variable.

It's not so much a bug, as it is an ambiguity introduced by the naming scheme that was given a default resolution method, which can be annoying if you have never encountered the problem before and m-lint doesn't mark it. Similar behavior occurs when variables are poofed into the workspace without initialization beforehand.

So the solution is to either change the name of the function or the variable to different things, which I would argue is good practice anyways.


In considering your follow-up example, I have noticed some interesting behavior in moving things around in the function. Firstly, if the function is either external or nested, you get the behavior discussed very well by Suever's answer. However, if the function is local, you can get around the limitation (at least you can in my R2014b and R2015a installs) by invoking the function prior to converting it to a variable as long as you initialize it or explicitly convert it to a variable at some point. Going through the cases, the following bodies of matlabBugTest perform thusly:

  • Fails:

    a
    if false
        a = 'foo';
    end
    a
    
  • Runs:

    a
    if true
        a = 'foo';
    end
    a
    
  • Runs:

    a = a;
    if false % runs with true as well.
        a = 'foo';
    end
    a
    

I'm not entirely sure why this behavior is the way it is, but apparently the parser handles things differently depending on the scope of the function and the order of what symbols appear and in what contexts.

So assuming this behavior hasn't and will not change you could try something like:

pathConstant = pathConstant;
if ~exist('pathConstant.m', 'file')
    pathConstant = 'C:\some\path';
end
load(fullfile(pathConstant, 'filename.ext'));

Though, entirely personal opinion here, I would do something like

pathConstant = getPathConstant();
if ~exist('pathConstant.m', 'file')
    pathConstant = 'C:\some\path';
end
load(fullfile(pathConstant, 'filename.ext'));

Concerning breaking "compatibility between functions and scripts/command line", I don't really see this as an issue since those are two entirely different contexts when it comes to Matlab. You cannot define a named function on the command line nor in a script file; therefore, there is no burden on the Matlab JIT to properly and unambiguously determine whether a symbol is a function call or a variable since each line executes sequentially and is not compiled (aside from certain blocks of code the JIT is designed to recognize and optimize like loops in scripts). Now as to why the above juggling of declarations works, I'm not entirely sure since it relies on the Matlab JIT which I know nothing about (nor have I taken a compiler class, so I couldn't even form an academic reason if I wanted).

Community
  • 1
  • 1
TroyHaskin
  • 8,361
  • 3
  • 22
  • 22
  • 2
    Interesting. I'm running R2016a which oddly doesn't give such a helpful error message. – tarheels May 26 '16 at 23:45
  • @tarheels That is indeed odd. It seems like a much cleaner error than what you got ... I may have to install R2016a to investigate. – TroyHaskin May 26 '16 at 23:46
  • @AndrasDeak Crap. That's what I get for not researching ... (^_^;) – TroyHaskin May 27 '16 at 01:06
  • Don't worry about it, even if this was a clear dupe (which I'm unsure about), both the question and answer are great to have around. Helps a lot with google-fu:) – Andras Deak -- Слава Україні May 27 '16 at 09:24
  • @AndrasDeak Thanks for linking that in. Unfortunately, I wasn't able to find the right search terms to locate that prior question. There's a bit of a catch-22 when trying to accurately categorize a problem for which you don't know the cause! – tarheels May 27 '16 at 14:37
  • @tarheels indeed, and I had a hard time finding the post myself, even though I've seen it recently:) I think this is a rare case when a duplicate is really helpful, since the question is well-formed and likely to lead to the solution (which is also here, this is a very nice Q&A pair in my opinion). – Andras Deak -- Слава Україні May 27 '16 at 14:51
  • 1
    @tarheels I've added more discussion in relation to your follow-up and some playing around. – TroyHaskin May 27 '16 at 21:00
1

The reason that you get this behavior is likely that Matlab never have implemented scope resolution for any of their statements. Consider the following code,

(a)

if true
    a = 'foo';
end
disp(a)

This would actually display "foo". On the other hand would,

(b)

if false
    a = 'foo';
end
disp(a)

give you the error Undefined function or variable "a". So let us consider the following example,

(c,1)

enterStatement = false;
if enterStatement
    a = 'foo';
end
disp(a)

(c,2)

enterStatement = mod(0,2);
if enterStatement
    a = 'foo';
end
disp(a)

TroyHaskin clearly states the following in his answer

It has to do with how Matlab performs name-binding at compilation time. Because matlabBugTest has a line that assigns a value to a, a is determined to be a variable, and the later line with a is a reference to that variable and not a call to the local function

Matlab does not support constant expressions and does only a limited amout of static code analysis. In fact, if the if statement takes argument false, or if enterStatement is false, Matlab provides a warning, This statement (and possibly following ones) cannot be reached. If enterStatement is set to false Matlab also generates another warning, Variable a is used, but might be unset. However if enterStatement = mod(0,2), so to say if enterStatement calls a function, you get no warning at all. This means that if the example in the question was allowed then (c,2) would compile based on how the function were evaluated and that is a contradiction. This would mean that the code would have to compile based on its runtime results.

Note: Sure it could be good if Matlab could generate an error in case the enterStatement was an expression instead of a constant false, but whether or not this is possible it would depend on implementation I guess.

Community
  • 1
  • 1
patrik
  • 4,506
  • 6
  • 24
  • 48