tl;dr: Don’t use the Mario Mushroom Operator if you don’t want your setter to be invoked more than once.
Update: I totally forgot to mention that I found out about this problem with @darscan while working on Swiftsuspenders and Robotlegs code.
The Details I think it was @robpenner who coined the term “Mario Mushroom Operator”.
In case you’re wondering, the Mario Mushroom Operator is this: ||=.
A good translation for how the MMO™ works seems to be this:
if (!field) { field = value; }
In case you’re still wondering, here’s how you’d use it:
function useRuntimeDefault(input:Object):void { input ||= getDefaultValue(); }
I.e., the most important use-case for the MMO™ is to apply runtime-determined default values for method arguments.
Now, that’s really useful and you might be wondering what could possibly go wrong with that.
Consider this snipped of code:
private var _setOnce : Boolean; private var _value : Object; public function set value(value : Object) : void { if (_setOnce) { throw new Error('Value can only be set once'); } _setOnce = true; _value = value; } public function get value() : Object { return _value; }
Used in a class, this code encapsulates a value that can be set exactly once, after which it can only be read.
With me still? Splendid.
In fact, you’re probably already guessing what comes now: Using the MMO™ to assign this once-settable value iff it hasn’t already been set:
value ||= {};
And here, finally, things go awry. As it turns out, the MMO™’s translation given earlier isn’t quite correct. Instead, the compiler (or the VM, I haven’t checked the bytecode) seems to translate our usage of the MMO™ to something along the following lines:
field = field ? field : {};
In summary, instead of guarding the assignment to a field as an if statement would do, the MMO™ only chooses between two values to use in the assignment like the ternary operator does.
In case you’re now thinking that that doesn’t affect you because you, just as every sane person you know, don’t ever have use-cases for one-time assignable fields, ask yourself whether you can be certain that all your setters are side-effect free if you re-assign the same value and that you never, ever, care about the overhead associated with double-setting values needlessly. “Yes” and “yes”? Cool. “Mmh” and “not sure”: Weep with me.