We all know the benefits of setting default class-property values: it saves coding, increases class readability, improves maintainability and reduces the potential for coding bugs due to uninitialized properties. Basically, we’re setting default values of the class properties, so that whenever a new instance of this class is created, it will be recreated with these same default property values. This is the behavior in any self-respecting OOP language, and is a well-entrenched paradigm in OOP computing. Simple enough, right?
Well, unfortunately it doesn’t behave quite this way in Matlab…
Problem example
First, define class Internal
as follows:
classdef Internal < hgsetget & matlab.mixin.Copyable properties Value = 0 end methods function obj = Internal() end function set.Value(obj, newValue) obj.Value = newValue; end function str = char(obj) str = sprintf('Internal [Value=%s]', mat2str(obj.Value)); end function disp(obj) disp(char(obj)); end end methods (Static) function obj = getDefault() obj = Internal(); end end end |
Now define class External
as follows:
classdef External < hgsetget & matlab.mixin.Copyable properties MyValue = Internal.getDefault; end methods function obj = External(varargin) % empty constructor end function set.MyValue(obj, newValue) obj.MyValue = newValue; end function str = char(obj) str = sprintf('External [MyValue = %s]', char(obj.MyValue)); end function disp(obj) disp(char(obj)); end end end |
Now run the following (note the highlighted unexpected internal value):
>> clear classes >> e1 = External e1 = External [MyValue = Internal [Value=0]] >> e1.MyValue.Value = 1 e1 = External [MyValue = Internal [Value=1]] >> e2 = External % This returns a bad value of 1 (should be 0!) e2 = External [MyValue = Internal [Value=1]] >> e1 == e2 % this proves that e1~=e2 (as expected) ans = 0 >> Internal.getDefault % this proves that the default Internal value remains unchanged (as expected) ans = Internal [Value=0] |
Basically, this shows that setting e1.MyValue.Value=1
has also affected future class constructions, such that e2=External
(which should have created a new handle object with a default property value of 0) now returns the modified value 1.
How is this possible?
The answer lies in the fine print of the documentation:
Evaluation of property default values occurs only when the value is first needed, and only once when MATLAB first initializes the class. MATLAB does not reevaluate the expression each time you create a class instance.
In other words, when Matlab first loads the External
class code into memory (typically upon its first instance creation), it assigns the handle reference to a new Internal
instance to the class property. This same handle reference (memory pointer, in a broad sense) is used for all subsequent creations of External
class instances. So basically, all instances of External
share the same Internal
object!
This does not affect properties that are initialized to primitive (non-object) data, nor to value classes (as opposed to handle classes). The reason is that Matlab’s built-in COW (copy-on-write) mechanism ensures that we get a new copy of a value class whenever one of its properties is modified. However, this does not happen in handle classes, so modifying Internal
‘s property in one instance of External
also affects the other instances.
So yes, it’s fully documented (sort of). But confusing? Counter-intuitive? Unexpected? – you bet!
In recent releases, Matlab increasingly relies on MCOS (Matlab’s latest OOP incarnation) for its internal codebase. I venture a guess that if a poll was made among MathWorker developers, the majority (if not vast majority) of them are not aware of this fine detail. Certainly programmers who come from other programming languages would not expect this behavior. This raises a concern that any internal Matlab object that has properties which are handle objects might incur hard-to-trace bugs due to this behavior.
Workaround and plea for action
At the moment, the only workaround is to programmatically set the property’s default value to a new instance of the handle class in the classes constructor. So, in our case, we would modify External
‘s constructor as follows:
classdef External < hgsetget & matlab.mixin.Copyable properties MyValue % no misleading default value here! end methods function obj = External(varargin) % non-empty constructor obj.MyValue = Internal.getDefault; end... |
I strongly urge MathWorks to modify this unexpected behavior (and certainly the documentation). I believe that the vast majority of MCOS users would welcome a change to the expected behavior, i.e., create a new Internal
object at instance creation time rather than just at class-load time. Moreover, I bet that it would save many internal bugs in Matlab’s own code, which is probably by far the largest MCOS codebase worldwide…
I’m the first one to complain about backward incompatibilities, but in this particular case I think that it’s not an important concern since users should never rely on this “feature” in their code – it would be bad programming for so many reasons.
Reference: G1308623
Addendum April 11, 2015: Dave Foti (who heads MCOS development at MathWorks) and Sam Roberts (a former MathWorker) have provided important insight as to the reasons for this confusing behavior (read the comments below). Once we understand it, we can actually use it in interesting ways, to differentiate between class load-time defaults (in the properties
section), and class-instantiation defaults (in the constructor). As noted below, MathWorks could probably do a better job of alerting users to the potentially confusion behavior, using either an MLint editor warning, and/or a run-time console warning (preferably both).
Wow, I wasn’t aware of this.
I agree, this is terrible : a different behavior depending on if you initialize in the constructor or in the property list!
We had the same problem last week. The only difference was, that we called the constructor directly, but the behaviour is the same.
This problem is also very difficult to debug, because there is no convenient way as far I know to get the objects id in Matlab (like in Java with System.identityHashCode or ==), to see references to the same object. It would be perfect if itβs possible to show the reference address of handle objects in the Workspace viewer of Matlab.
I’m also in favour to modify this behaviour in Matlab. For me it’s a bug and not a feature.
While handle ID’s are not visible in MATLAB, == does compare two handle ID’s, provided the specific handle class hasn’t defined an override for the eq function. This can be used to ask if two handles refer to the same object.
Hi Yair,
You can reproduce this issue with a much simpler example:
I also have been bitten by this issue, and I agree that it can seem unexpected when you first come across it, especially if your previous experience is in other languages that don’t behave in this way. Nevertheless, this is the first time that I think I disagree with your conclusion.
i) although it can be unexpected the first time you come across this behavior, it’s the sort of thing that doesn’t really bite you twice. Once you’re aware of it, you just know about it and it’s not really a problem. I can guarantee to you that any MathWorks developer who works in an OO style is aware of this behavior, so changing it is not going to save MathWorks from any significant number of bugs.
ii) it is actually well-documented – just not on the page that you linked to. Go to that page and follow the link immediately underneath the piece you quoted, and you’ll come to a section “When MATLAB evaluates expressions” on the page “Expressions in Class Definitions”. The issue is really very thoroughly explained, with a couple of worked-through examples.
iii) there are some sensible use cases for this feature (IMHO it is a feature), and some reasons why it’s a good design for MCOS. These are specifically related to the way MATLAB is typically used, that stand in contrast to other languages.
For example, it is very common in MATLAB that one would rapidly implement different versions of a class, changing features all the time. Perhaps these might be statistical models, for example. At each stage you might want to create objects of the class, and serialize them to a .mat file. Later on, you might want to load in the old .mat files, after the class definition has changed. This is a very common workflow in MATLAB, because it’s not just a language – it’s an interactive environment for data analysis; but it would be very uncommon in a language such as Java or C. Implementing this sort of class-definition backward-compatibility is significantly easier if you take advantage of property defaults, instantiated at class-loading time. There’s an example of how and why you might want to do that on the doc page I mentioned above.
By the way, this issue is part of a more general issue of the way MATLAB handles expressions in class definitions, and if you think this behavior is confusing, wait until you find out that you can also have expressions in property, method and class attributes. For example, it’s legal to say classdef (Sealed = logical(rand<0.5))) MyClass. Try doing that, then run clear classes and ?MyClass over and over π
Sorry, my less than and greater than symbols seem to have come through as HTML tags for some reason. Hopefully you’ll all be able to guess what I meant.
Thanks for your detailed comment, Sam. I’m flattered that in 6 years this blog has been alive this is the first time you disagree with me, it says something coming from you π
I still think that this so-called “feature” is confusing and should be changed, regardless of whether or not MathWorkers are aware of it (I’d still be willing to wager that most of them are not, but that’s a minor issue). You yourself have said it has bitten you in the past, and so was I, and so apparently were others. I’m betting that the number of people who were bitten by this (including numerous people who are not even aware that they were bitten, since it results in very hard-to-trace bugs!), far outnumbers the number of people who serialize classes to MAT file, and then load them in the same Matlab session. The ratio between these two populations is probably greater than 100:1, IMHO.
This “feature” is no different than any other bug that has a workaround, and typically only bites you once since you’d know about it after that point. The case of needing to place a simple drawnow to avoid a Matlab hang when using standard dialog windows, is another example of something that is extremely difficult to trace and debug, and only bites you once since you’d automatically avoid the problem in the future. I think you’d agree that it cannot be considered a beneficial “feature”. Nor can this one I believe.
At the very least, Matlab should issue a warning when loading such a class with an internal handle property, so that users could at least know about it and not waste endless hours chasing after illusive bugs.
Well – we’ll agree to disagree, I guess.
Just another brief point, though: this is not just an issue with using a handle object as a default property, it’s an issue with evaluating any expression as a default property. For example, you can set a property to have default value of datestr(now), and all objects will by default be given the time that the class was first instantiated. The expression can potentially be as complex as you like. In such a case, evaluation of the expression at class-loading time can also be advantageous: for example, if you have a Constant property with such a complex expression as its default, there’s no point in evaluating it every time an object is created, so you can do it just once at class-loading time.
I suspect you may regard that as another very small side-benefit that doesn’t justify confusing behaviour, though π
Thanks Sam – this is indeed a correct and important clarification.
I’ve been bitten by this behavior, too. I wanted each object to have a different handle object on a property, and default property values initially seemed like the easiest way to do that. But upon reflection, I don’t think the current behavior should change. In fact, I think Sam’s example (Hi, Sam!) using
datestr(now)
perfectly illustrates what’s going on and why the current behavior makes sense. The whole point of a default value is so that it’s the same default for all objects. This can be confusing when you’re talking about handle objects. In your example, it sounds like you want each External object that’s constructed to also construct a different Internal object. But if you think about it, doesn’t that sounds exactly like something that should happen in a constructor?For what it’s worth: for “somewhat” experienced Python programmers this feature may be less surprising than to others.
The same principle in Python applies to default function arguments (which don’t exist in matlab) and I’d guess that the python/matlab internal reason for it being this peculiar way might be identical: Namely that the class definition is parsed at the some time, creating the corresponding metaclass object and simply assigning the default property values on each newly created instance of the class – using the normal copy semantics of the corresponding type.
This Stackoverflow question reveals other advantages and disadvantages and speculations about the reasons (if any) behind this decision.
While I understand the source of confusion, I think Sam and Sebastian have provided most of the rationale for why MATLAB works the way it does. The class definition is evaluated once while constructors are executable functions that execute every time an object is instantiated. This gives the programmer the flexibility to choose whether an expression is evaluated once per class (in the definition) or once per object (in the constructor). In addition to the referenced benefits of having expressions evaluated only once, the idea with the class definition is that it provides information that can be queried by tools and used to help document the class. For example, because the
meta.property.DefaultValue
is a value, you can compare this value to the current value of an object property and you can ask what value will I get if I create an object. If default values are expressions that need to be evaluated every time, it is difficult for tools to query or display default values without causing unwanted side effects and a tool can’t be sure what specific value a new instance will get by default.I agree that MATLAB could do a better job providing hints in the editor when assigning default values to handle objects or expressions that can return different values every time they are evaluated.
@Dave – thanks for the insight. I was hoping you’d pitch in at some point, and I’m happy that you did. I’ve updated the post text with a corresponding addendum. I hope that you do add the MLint warning and preferably also the runtime console warning, so that users would no longer be confused. Once users are aware of the distinction between class load-time and instantiation-time defaults, I agree that this could well be put into good use.
Might this not be handled by introducing a property attribute to determine the behaviour?
or something along those lines? I am quite unhappy with the current behaviour, and for my particular use I find the workaround (initializing in the constructor) to be ugly.
That would be a very good solution for this issue. The default value for this property should be true because the described use cases where this Matlab behaviour is advantageous are only useful for some special tasks.
To me this seems like a syntax issue… The behavior seems to be equivalent to Java’s static class level properties instead of the regular object instance properties. I don’t think the behavior absolutely needs to be changed but the way the declaration syntax should reflect this distinction. A feature? Definitely. But it introduces ambiguity. I agree with Yair that it’s best to have it changed… At the very least, the syntax of a class level property (static?) should be different from an instance-level property initialized within the constructor. This will benefit programmers that come from OOP background; at the very least, Java programmers like myself.
Is anyone aware if this is still the behavior in the newest Matlab version, 2016b? We have a very large suite of classes that now need to be sifted through to verify we’re not using this construction methodology. I’d guess we probably aren’t otherwise we’d have seen an error by now, but who knows, because I don’t know why we wouldn’t. Default handle properties are certainly a typical use case and one would not expect said property to be the the same instance of in all calls.
Yes, R2016b has the same behaviour.
Thank you man! you saved me, when there was no more light π