Why am I warned about defining variables in a nested function, when I don't?

  1. EDIT: The following is an newer and simpler description of the problem:
Every time I run my function warnTest(), I receive a warning about defining the variable "ind" in the nested function. But I don't define the variable "ind" in the nested function. The warning doesn't seem to be true.
Furthermore, why am I not warned about the variable "n", which is also defined in the parent function and shared with the nested function, just like the variable "ind"?
function warnTest()
% % warnTest() reproduces the following warnings:
%
% Warning: File: /path/to/warnTest.m Line: 27 Column: 29
% Defining "ind" in the nested function shares it with the parent function. In a future release, to share "ind" between parent and nested functions, explicitly define it in the parent function.
% Warning: File: /path/to/warnTest.m Line: 27 Column: 42
% Defining "ind" in the nested function shares it with the parent function. In a future release, to share "ind" between parent and nested functions, explicitly define it in the parent function.
%
% However, warnTest() doesn't define the variable "ind" in the nested
% function, as suggested by the warning message.
%
% Additionally, warnTest() also defines the variable "n" in the parent
% function and shares it with its nested function. However, MATLAB issues
% no warning about the variable "n".
%
% This behaviour has been observed in the following MATLAB Releases:
%
% MATLAB® R2018a Update 6 (9.4.0.949201) 64-bit (maci64) September 5, 2018
% MATLAB® Version: 9.6.0.1150989 (R2019a) Update 4 64-bit Windows 10
% Define labels for divisor and dividend
f = ["a","a","a","a","a","b","b","b","b","b"];
l = numel(f);
k = unique(f);
n = numel(k);
m = 7;
p = 5;
% Define divisor and dividend
A = randi(100,m,l);
B = randi(100,p,l);
% Define var ind in parent function
for ii = n:-1:1
ind{ii} = find(k(ii) ~= f);
end
C = nestFun(A,B);
disp(C);
% Define nested function
function [Z] = nestFun(X,Y)
for jj = n:-1:1
Z(:,:,jj) = X(:,ind{jj})/Y(:,ind{jj});
end
end
end
  1. EDIT: The following is an older, slightly more complicated description of the problem:
Every time I run my parentFunction.m, I receive 2 warnings like the following:
Warning: File: /path/to/my/parentFunction.m Line: 439 Column: 34
Defining "train_ind" in the nested function shares it with the parent function. In a future release, to share "train_ind" between parent and nested functions, explicitly define it in the
parent function.
> In callerFunction (line 76)
Warning: File: /path/to/my/parentFunction.m Line: 439 Column: 59
Defining "train_ind" in the nested function shares it with the parent function. In a future release, to share "train_ind" between parent and nested functions, explicitly define it in the
parent function.
> In callerFunction (line 76)
However, the variable "train_ind" is explicitly defined in the parent function, before the nested function is first called.
So, why would I receive that warning, if it doesn't apply to my function?
(Then again, the warning is thrown in the callerFunction, in the line 76 which calls the parentFunction.m. Should that make a difference?)
My release: MATLAB® R2018a Update 6 (9.4.0.949201) 64-bit (maci64) September 5, 2018
The parentFunction.m is fairly long, so I'll show you the relevant lines only.
  • In line 241 I first define the variable in question:
% Build training & testing index for cross-validation
for ii = n_folds:-1:1
train_ind{ii} = find(k(ii) ~= folds);
test_ind{ii} = find(k(ii) == folds);
end
  • In line 256 I first call the nested function:
% Train encoding model for the entire brain
weights(:,:,:,ff) = IEM_train(betas, des(:,:,ff));
  • In line 429 I define the nested function:
function [weights] = IEM_train(betas, des_mat)
% Train encoding model with cross-validation
% Initialise weights
weights = NaN(n_vert,n_chans,n_folds);
% Loop over cross-validation folds
for nn = n_folds:-1:1
% Compute weights for each fold
weights(:,:,nn) = betas(mask,train_ind{nn})/des_mat(:,train_ind{nn});
end
end

6 Comments

It is veeeery long (for my standards :D)... (see Line number 400). How about I extract the relevant lines?
As usual, please state which MATLAB release this refers to.
@Ameer Hamza, I have added the relevant snippets of code to my question.
@John D'Errico My release: MATLAB® R2018a Update 6 (9.4.0.949201) 64-bit (maci64) September 5, 2018
My copy (R2020a_u5 on W10) doesn't return any warning (mlint or otherwise) when running your example function.

Sign in to comment.

 Accepted Answer

Hello everyone,
It appears so, that this warning does not persist for release R2019b and later releases when cell array 'ind' is implicitly initialized as in the provided code.
The trigger of this warning in the prior to R2019b releases seems to be the fact that cell array 'ind' is not explicitly initialized in the parent function.
By explicitly initializing array 'ind' before the loop where its elements are being assigned in the parent function, namely,
ind = cell(n, 1);
for ii = n:-1:1
ind{ii} = find(k(ii) ~= f);
end
the warning disappears also in the releases prior to R2019b.
Best Regards,
Andreas

3 Comments

That is .... subtle. So it hangs on the word "explicitly" in the warning "Defining "ind" in the nested function shares it with the parent function. In a future release, to share "ind" between parent and nested functions, explicitly define it in the parent function." Of course JMM did define the variable in the parent workspace, but only implicitly by indexing into it.
This now makes sense. Many thumbs up to Andreas for explaining this. The simple answer is thus to upgrade MATLAB, but the other takeaway is to always preallocate grown arrays. And we should be doing that anyway.
Thanks a lot John, I am happy I could help.

Sign in to comment.

More Answers (2)

Because nested functions share the name-space with its parrent function. That is all variables in the parrent function are available in the nested function - pretty much as global variables. Apparently variables defined in a nested function also become available in its parrent function.
(There might be good use-cases for nested functions, but the "global" nature (between parrent and nested function) of the variables made me stay away from nested functions - it is, in my experience, possible to get by with sub-functions in all cases. The global-nature will obvoiusly come with similar problems as global variables in general - they might randomly change variables rather far away from where the problems arise, which makes debugging challenging. It took me some 15-20 years to weed out global variables from a toolbox I started with before I learnt how much problems they might cause. To be fair for the nested functions the problem is way more contained than for global variables in general. But, again "in my opinion", the problem is of the same character, and you have at least a 400-lines file. I guess the warning is there to tell you what will/might happen in future releases. I'd switch to a subfunction - but you might very well have good reasons for using a nested function...)
HTH

10 Comments

Here's an example of Bjorn Gustavsson's excelent point,
x is defined as 1 in the main function but redefined as 9999 in the nested function. That value caries over to the main function and displays '9999' as a result. If you remove x=1 you'll get the warning message described in the question (or an error in recent releases).
function fun()
x = 1;
nestFunc();
disp(x)
function nestFunc()
x = 9999;
end
end
Result:
>> fun
9999
In fact, Matlab tries to warn you about this by coloring the variable "x" the same color that global variable have.
"Here's an example of Bjorn Gustavsson's excelent point"
Note what Adam Danz's example shows is just the normal, expected, documented behavior of nested functions, and does nothing to explain the warning which was given in the question. For that matter, nor does Bjorn Gustavsson's answer actually explain the warning that the OP reports.
"In fact, Matlab tries to warn you about this by coloring the variable "x" the same color that global variable have."
Syntax highlighting is not a "warning". A "warning" is given when something does not (or might not) work as expected, not for when something works just as it is supposed to.
The entire point of using nested functions is to share variables between the function workspaces, so "warning" is a misleading term to use, when sharing between workspaces is their intended purpose.
Variables that are shared are highlighted to make it easier to track them, but it is not a "warning" anymore than the color of the keyword FUNCTION is a "warning" that a user has written a function, or the color of the keyword IF is a "warning" that the user needs to write a condition, or the color of PERSISTENT a "warning" that some values might hang around between function calls.
"Apparently variables defined in a nested function also become available in its parrent function."
Only when they are also defined in the parent workspace. In fact the MATLAB documentation clearly states: "When parent functions do not use a given variable, the variable remains local to the nested function." This can also be tested quite easily.
Since I avoid using nested functions I must say that I was simply reading off the warning-message - that I took to claim that variables defined in the nested function would become shared with the parrent function. A simple test shows that there is some fringe-behaviour with nested functions and variable-scopes:
function x = nested_test()
% NESTED_TEST - Test of nested function behaviour
%
Var1 = 12;
whos x
Var2 = nested_fcn(Var1);
whos x
function Var2 = nested_fcn(VarIn)
% NESTED_FCN - Test
%
Var2 = VarIn+1;
x = pi*VarIn;
end
end
When I run this in matlab 2020a this results in:
>> X = nested_test
Name Size Bytes Class Attributes
x 1x1 8 double
X =
37.6991
So only the second call to whos acknowledges the existence of x, and it is popped into existence in the parrent function with the call to the nested function. With a slight modification of the test-function:
function [y] = nested_test()
% NESTED_TEST - Test of nested function behaviour
%
Var1 = 12;
whos x
Var2 = nested_fcn(Var1);
whos x
y = x;
function Var2 = nested_fcn(VarIn)
% NESTED_FCN - Test
%
Var2 = VarIn+1;
x = pi*VarIn;
end
end
matlab throws an error. It seems to me that it is enough to inform matlab about the intension to return a variable x for matlab to let me pop a variable into existence in the parrent-function from a nested function, which appears a bit dubious to me, in particular since the second variant throws an error. My lesson for today is that I will continue to avoid nested functions.
For me personally there are two reasons to avoid nested functions:
  1. Avoiding confusion. I prefer not to deal with variables that I happen to be using in the nested function as well, even if the syntax coloring would warn me.
  2. Compatibility with GNU Octave and Matlab 6.5. I like to keep everything (or most things) compatible with those two. Octave because I want to allow people to use my code in places where Matlab is suboptimal (e.g. distribution without commercial backing, or on USB media). R13 is in there both because I like the challenge and because it is still the fastest option for specific tasks (especially graphics creation).
I do think nested functions have a place in Matlab (especially in sharing variables in GUIs), but this thread is a testament to the point that they can make things pretty confusing when they go awry.
Thanks a lot - these are all very helpful comments!
I am still confused about the fact that the variable in question ("train_ind") is never actually defined in the nested function. It is only ever defined in the parent function.
This is unlike another variable - "weights" - about which MATLAB doesn't warn me, though. Isn't that odd?
"...appears a bit dubious to me"
Yes, it all depends very much on the meaning of "use" in the documentation: clearly there are many ways that a variable can be referred to (or "used"), which allows some ambiguity in the scoping interpretation.
BTW I agree that sharing variables between nested and parent functions is tricky. I may do it differently, if I had to start the project all over again. My reasons for sharing variables are the ease of reading, the consistency of variable names, and - to be honest - simply the length of a call to a function, that requires many input arguments, which could be shared, too.
I have adapted the title of my question to reflect the oddity, that MATLAB sends me a warning about something which does not seem to be the case.
@Stephen: and it seems to vary a bit between versions, in my first version the variable x deeper in limbo than a variable specified with persistent that's not been assigned to anything...
@JMM: for that use I stick to sub-functions and bear the burden of the longer argument-lists - and have the relief to know that I don't have to worry about peculiar side-effects of my functions (and with matlabs call-by-reference-until-modified I'm under the impression that there is not much additional overhead.)

Sign in to comment.

So far no one has actually explained the warning, which is a pity because this is really quite an interesting question.
Some answers/comments have explained the basic concept of nested functions (Surprise! Nested functions can share variables between workspaces!), but so far no one has actually put forward a plausible explanation of this warning in the context given in the question.
Here are a few possible explanations of this strange warning:
  • Some version-specific warning. Perhaps it was decided to remove this (rather superfluous?) warning, so it only appeared in one/a few releases.
  • Some special syntax that confuses the mlint parser, so that it cannot recognize that the variable is also defined in the parent workspace. For example, does the code include any (awful, anti-pattern) magic like eval, assignin, or any debugging commands? Does the code use cells or anything similar? Is it written in a script?
  • Is the variable name also the name of a function? Check using which.
  • Spelling. Yes, really. For example, it is very easy to miss a different character case: is it really x defined in the parent workspace, or is it X ? You can use the syntax highlighting to help check this: https://www.mathworks.com/help/matlab/matlab_prog/check-variable-scope-in-editor.html
If you want more help on this please tell us the exact MATLAB release you are using and upload the code.
EDIT: as little bit of internet searching found this, which might be related:
https://www.mathworks.com/help/matlab/release-notes-R2017b.html -> Language and Programming -> Functionality being removed or changed -> "Sharing uninitialized variables between a nested function and the parent function"

7 Comments

This started issuing a warning in release R2018a and started throwing an error in release R2019b, according to my tests using the following function:
function fun598636()
% x = 1;
y = 2;
nestFunc();
disp(x)
disp("whos in main function")
whos
function nestFunc()
x = 9999;
z = 3;
disp("whos in nested function")
whos
end
end
See this page for the details of the change in release R2019b.
@Steven Lord: your example does not define x in the parent workspace, but JMM clearly states "However, the variable x is explicitly defined in the parent function, before the nested function is first called." Their updated question also shows this.
Based on the JMM's comment "...the variable in question ("train_ind") is never actually defined in the nested function. It is only ever defined in the parent function." your test code does not reflect the actual code very well. More representative test code would be something like this:
function fun598637()
x = {11,22,33};
nestFunc();
whos
function nestFunc()
disp(x{3})
end
end
And for this more representative code, the warning message shown in the question "Defining "train_ind" in the nested function shares it with the parent function. In a future release, to share "train_ind" between parent and nested functions, explicitly define it in the parent function" does not make a lot of sense. Unlike your example, JMM already did explicitly define the variable in the parent workspace, so why the warning?
Still no answer.
These are all excellent points! Let me take them in turn.
  • The variable "train_ind" occurs exactly 3 times in my parentFunction.m. Once in line 243, where it is defined and twice in line 439 within the nestedFunction (it's called IEM_train...), where it is used to index another variable. I checked this with variable highlighting and the search utility.
  • Although there are no uses of assignin() or eval() or the like within the parentFunction.m the function, which calls the parentFunction.m - let's name it callerFunction.m - uses these two commands a lot for dynamic variable assignment and saving. I wish it didn't because it is very bad practice, but I was too lazy to spell out over 200 variables manually (and too interested to learn, how dynamic variable assignment works). However, the warning message has been annoying me for a long time now, across many version of the code, including ones without dynamic variable assignment.
  • Almost always, I run my code with the debugger set to "dbstop if error". There are no debugging commands within the code.
  • All the code are MATLAB functions, there are no scripts.
  • My parentFunction uses some cell arrays, is that what you meant by "cells", @Stephen Cobeldick?
  • I didn't find any naming conflict for the variable in question:
>> which train_ind
'train_ind' not found.
EDIT: I don't really NEED a solution to my problem, i.e. I don't really NEED the warning message to disappear. I am just puzzled why it's there. In my experience so far the computer had always been right in the end. Now, I don't yet see, where I am going wrong.
EDIT: I have to correct myself: I assign some fields of a structure in the following way:
fname = 'myFieldName';
myStruct.(fname) = zeros(5);
Would that count as "dodgy stuff" which could trip up the mlint parser?
@JMM: my guess is that this is an mlint "feature", and you should submit a bug report about it.
You will have to create a very simple working example, something like in my last comment, explain that the warning is nonsense in that context and make it very clear that the variable is defined in the parent workspace!
PS: sorry, I meant "code sections", which are (rather confusingly) also known as "code cells":https://www.mathworks.com/help/matlab/matlab_prog/run-sections-of-programs.html
PPS: no, not dodgy at all, quite the opposite! I doubt that the mlint parser would be confused by it.
@Stephen Cobeldick: Indeed, I have various code sections. I will start experimenting and see, whether I can produce a simple example of that odd warning behaviour.
@Stephen Cobeldick: I wrote a simpler function which reproduces my problem on two different installations & systems. I have added it to the top of my question. It doesn't seem to be related to eval(), assignin(), debugging settings, or code sections. I have also submitted a bug report to Mathworks.
@JMM: good work on eliminating those possibilities. So far it really does seem that a (recently introduced) warning is either a) worded incorrectly and needs to be re-written, or b) is being shown as a result of a false-positive match by the mlint parser. I tried your example on earlier versions of MATLAB, with no warning.

Sign in to comment.

Categories

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!