Particularly strange bug using the eval function

6 views (last 30 days)
In the MWE below, I define a bunch of parameters using an eval command. I know that the experts recommend against using eval, as in this thread, but I don't really see an alternative in this instance. I also know that MATHWORKS would prefer me not to define variables that clash with their own builtins. However, the code still should work properly, but doesn't in this example.
The class for rho is correctly defined as double. But the fourth last line returns an error, as does the second last line. In both cases the code still thinks gamma is matlab's built-in function. Yet the third last line indicates that gamma is correctly defined as 0.6. Note that if I remove the first line of the code, and thus run the code in the base workspace, everything works as expected.
Could somebody please explain a) what's causing this bug and b) a simple alternative to my use of the eval function?
function nothing
rhoY = 0.8;
phiY = 0.8;
gammaY = 0.6;
etaY = 0.05;
muY = .5;
ecLetter = 'Y';
Params = {'phi','rho','eta','gamma','mu'};
nParam = numel(Params);
for ii=1:nParam;
thisParam = Params{ii};
eval([thisParam '=' thisParam ecLetter ';']);
end;
%This kludge fixes the problem
tmp=gamma;
gamma=tmp;
class(rho)
class(gamma)
gamma
3*gamma
keyboard;

Accepted Answer

per isakson
per isakson on 2 Aug 2020
Edited: per isakson on 2 Aug 2020
I reproduced the issue you report on R2018b, Win10.
I noticed that if
  • I put a breakpoint at the line "class(gamma)"
  • run the function, nothing
  • execute "class(gamma)" by select, right click, Evaluate Selection
  • "class(gamma)" returns the result that you expect
The issue has something to do with the Just-In-Time (JIT) accelerator, I think. You could report it as a bug. (However, DB's answer make me believe they already knows about a bunch of similar cases.)
IMO:
  • Experiments with this kind of code is a waste of time. However, the group working on the JIT should be interested in your observation.
  • "but I don't really see an alternative in this instance." Describe "this instance" and someone here might come up with a better alternative.
  9 Comments
Stephen23
Stephen23 on 2 Aug 2020
Edited: Stephen23 on 2 Aug 2020
"You could report it as a bug"
Not a bug, rather a fix to earlier ambiguous syntaxes. The general problem is NOT just related to changes in R2019b, because these ambiguous syntaxes have existed in many forms:
I would not recommend using any kind of shadow script: that is just sweeping the hackery under the carpet, but does not actually fix the root cause: your "solution" will always be fragile and susceptible to any changes in precedence rules with past and future versions, as well as fragile to any changes in the path, the MATLAB installation, your own code distribution, etc.
The robust solution is to write code that does not magically name variables.
dpb
dpb on 2 Aug 2020
Edited: dpb on 2 Aug 2020
The OP's lament that common mthematical variables are builtin MATLAB functions is one of reality and I agree it is unfortunate that Cleve et al., didn't think of naming the functions differently initially to preserve the names for users as variables. But, hindsight is 20:20.
Name spaces or a dictionary something like Forth uses would be one way to be able to reduce some of the namespace pollution problem -- it is getting really crowded in here any more with all the new functionality even in the base space and the few toolboxes I have; by the time one has a full collection it must be getting difficult to not have name clashes.
It's not helped by the recent introduction of the multitude of similar functions for the same thing as were already provided...instead of fixing the existing ones. And, of course, hardly anything ever gets removed.
As for solving OP's problem, I think it is probably best to simply recognize that MATLAB is what MATLAB is and "go with the flow" and give in gracefully. The hacks to beat Mother Nature just aren't worth the effort; it's not like you can't accomplish the end objective otherwise.

Sign in to comment.

More Answers (4)

Walter Roberson
Walter Roberson on 2 Aug 2020
This is defined behavior for the execution engine for the last few releases, and is not a bug. R2018b is the release that is springing to mind as when the change was made, but that would have to be re-checked.
The behavior defined is this:
Inside a function (and you are inside a function), when you have a reference to a name that MATLAB can see as a function on the search path, and there is no "plain text" assignment to a variable of that name, then MATLAB will now assume that plain-texts references to the name always refers to the function, even if the name is being assigned to by some side effect such as eval() or evalin() or executing a script.
A way of thinking about this is that MATLAB is now doing static analysis at the time the file is parsed, ignoring any potential non-obvious assignment.
The work arounds include:
  • making a plain-text assignment to all variables that might be changed in non-obvious ways; or
  • put the hidden assignments into a function and return the variables from the function (but this can just postpone the problem); or
  • Don't Do That -- don't make hidden assignments to variables. For example, assign to fields of a struct inside a function and return the struct and access the fields; or
  • use a class with static methods to create what are effectively constants
  3 Comments
per isakson
per isakson on 2 Aug 2020
"This is defined behavior for the execution engine for the last few releases" I don't find it in the on-line Help Center. Where should I look?

Sign in to comment.


Matt J
Matt J on 2 Aug 2020
Edited: Matt J on 2 Aug 2020
I also know that MATHWORKS would prefer me not to define variables that clash with their own builtins. However, the code still should work properly
I don't know why you think it should work properly. That is expected behavior and is one of the chief reasons eval is a bad idea. Also, it is unclear how you are executing your code and what errors you see. In particular, you say the 2nd and 4th last lines throw errors. But that should be impossible. If the 4th last line threw an error, the code should never reach the 2nd last line.
One easy solution is to use structs:
function nothing
S.rho = 0.8;
S.phi = 0.8;
S.gamma = 0.6;
S.eta = 0.05;
S.mu = .5;
Params = {'phi','rho','gamma'}; %subset
nParam = numel(Params);
for ii=1:nParam;
T.(Params{ii})=S.(Params{ii});
end;
class(T.rho)
class(T.gamma)
T.gamma
3*T.gamma
keyboard;
Obviously, if the goals is to copy all the fields in S, you don't need to use a for-loop. You could just do
T=S;
  2 Comments
Leo Simon
Leo Simon on 2 Aug 2020
Thanks Matt, for various reasons I really don't want to use structs, though I see that it would solve the problem
Matt J
Matt J on 2 Aug 2020
It's hard to see what those reasons might be. There is nothing you can do with indivdual variables that you cannot do with structs. They are basically the same thing, except one has a dot in its name.

Sign in to comment.


dpb
dpb on 2 Aug 2020
From the doc--
Whenever possible, do not include output arguments within the input to the eval function,
such as eval(['output = ',expression]). The preferred syntax,
output = eval(expression)
allows the MATLAB parser to perform stricter checks on your code, preventing untrapped errors
and other unexpected behavior.
I don't see anything here that indicates any need for eval anyway over simply writing the explicit assignments; there are only five variables and it took four lines of code to write the loop and how much time and frustration debugging?
  7 Comments
Bruno Luong
Bruno Luong on 3 Aug 2020
Edited: Bruno Luong on 3 Aug 2020
A true solution is MATLAB would allow users to define constants, not variable with unchaged value, similar to macro in C; the constant can be visible within local/global scope.
Might be the JIT accelerator does it already internally (?) who knows, as they said speed optimization is their business.
For sure it would be nice where as that kind of optimization occurs or not, at least for professional programmer. ;-)
Walter Roberson
Walter Roberson on 3 Aug 2020
some day I should test..
It is possible to subsasgn in the middle of an expression, and if I recall correctly it does change a variable in that position (that is, the new value is used in later parts of the expression.) This is similar to the way that assignin('caller') can affect a variable in an expression. I have tested these a bit for variables.
But at the moment I do not recall that I have tested for hard-coded constants: their slot can be assigned to using subsref and anonymous functions, but does that change other copies of the same literal in the line? (Though I seem to recall that I did test that once and found it did not happen, unlike the classical FORTRAN bug in early days where you could modify constants)

Sign in to comment.


Bruno Luong
Bruno Luong on 2 Aug 2020
Edited: Bruno Luong on 2 Aug 2020
This is NOT a bug but documenented behavior from recent release
"If you intend to use x as a variable from data.mat instead of a function, explicitly declare it. Similarly, to use an identifier x as a variable obtained from a script, declare it before invoking the script. This new behavior also applies if the variable is implicitly introduced by the functions sim, eval, evalc, and assignin. "
The workaround as explained in this doc would be you "declare" your variables, e.g. initialize with variable name explicitly written down with empty value, then call EVAL to change the values. Then use it later.
I must admit that would defeat somewhat the goal.

Categories

Find more on Historical Contests in Help Center and File Exchange

Products


Release

R2019b

Community Treasure Hunt

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

Start Hunting!