A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://sektorvanskijlen.wordpress.com/2019/05/16/conditional-inversion-very-harmful-myth below:

Yoda conditions very harmful seems to be

There is a popular “coding style” item, mainly used in C and C++, but also in other languages that were based on C and have made the = and == operators being able to be confused. This relies on simply inverting the arguments for the operator. It’s known under a slang name, Yoda conditions.

Here is why you should absolutely avoid it, and before you try to educate anyone as to how good this idea is, you better read this whole below.

1. The pavement of the good intentions

In C language the assignment operation has been decided to use the = operator. It was a simplification of the statement known from other languages with let instruction, which written as let X = Y made sense as “let X become equal to Y”, but then someone thought that X = Y should be good enough. Then someone thought that the assignment can also leave a result in the form of the value itself being assigned, so it could be used in an expression, like A = X = Y . Therefore it needed to be distinguished from the “comparison for equality” expression, so for the latter the == (double equal) symbol was used, as in this case both X = Y and X == Y have a result and mean different things.

This has led to a confusion, as one might write mistakenly (a = b) instead of (a == b), which may lead to an undetectable error. This was also true for the “not equal” operator: != because the same problem may arise if you invert the order of ! and = and make something like a =! b, that is, still assignment, but you apply NOT operation on b first.

Note BTW. that Pascal and Ada have used a different approach: The assignment operator is:= – so a single = could stay as comparison operator. AFAIR they also don’t allow the assignment expression to be a part of a compound expression.

Someone has then spelled up a solution: as most of the comparisons are like VARIABLE == PATTERN, and the latter is constant, let’s invert this and put the constant value always at the left side of the operator, that is, PATTERN == VARIABLE. When you do this, then a mistakenly used single = will be always detected by the compiler as an attempt to write into a constant and reported as error.

Unfortunately, probably the inventor of this solution didn’t even predict how his idea will turn into an utter nightmare.

2. The practice of identification

The natural way for writing conditional expressions is always in this order:

This is because it is so simply when you speak your natural language. If you want to speak about a condition as to whether it applies to some object, then you always first mention the object itself, then the condition to be tested. It doesn’t even matter if your language is SVO, SOV (even partially or conditionally) or even VSO – the order “first S then O” is always preserved, at least in the vast majority of languages in the world. The only exception is when you want to highlight the object, which only happens in an exceptional situation when your context is that it follows some already suspected condition on that object.

Let’s then take some example expressions:

a == b
p == 0
x.find(c) == -1
x.find(c) == b
produced() == consumed()

Here we want to apply – seriously – the “assignment prevention” rule, that is, we want that a non-assignable expression be on the left side of the == operator, and nothing else at all. This means that we might want to invert the order of arguments of the == operator, but only if after the inversion there’s a non-assignable expression on the left and assignable expression on the right. In all other cases, we want the rule of the above described natural order to be preserved. So, the correct form after this reordering is:

a == b // inversion is useless - both are variables
0 == p // ok, variable on the left replaced with constant
x.find(c) == -1 // expression on the left isn't assignable anyway!
x.find(c) == b // idem, and there's a variable on the right!
produced() == consumed() // both are values!

Unfortunately every time when you look at the code written using this rule looks like this:

b == a // useless
0 == p // correct
-1 == x.find(c) // useless, constant is also on the right
b == x.find(c) // WRONG! variable has been SHIFTED to left!
consumed() == produced() // useless

But then… what’s the common treat of all these expressions here in this second version?

Well, the inversion has been done: the natural order TESTED OP PATTERN has been inverted into PATTERN OP TESTED. Actually, this isn’t how this rule sounds, not even approximately, it’s not even similar to the original rule. But this is how this rule is “executed”.

But why?

Well, the answer is: because this is how the human brain works.

3. Morons or automata

Are programmers in their majority such sorry morons that they cannot understand a simple rule?

During my working in Motorola (today: Nokia Networks) in Krakow I liked this rule in the beginning and in the code peer reviews I have tried to insist on that rule very seriously. I was reporting problems with misuse of this rule and were trying to insist on that it is adhered to the letter, which means that:

The problem never was in case when the inversion required to invert the natural order. In such case no one was arguing. The problem was when my finding applied to an expression that has actually inverting the natural order and the fix had to revert it. If this was because the order inversion has actually “impaired” the expression, it was nothing to argue because their error was evident. Arguing was just in the case when the inversion was only useless. In this case I could only insist that the natural order is better and more readable for an average user and the inversion makes us no good. They were disagreeing just because “this looks better” or they “got familiar to this order because of the assignment prevention rule”. How?

Worse than that. They were inverting not only in “useless” cases using the == or != operators. Also in cases when the inversion shouldn’t be even in question at all:

0 <= x // RELATIVE OPERATOR, no assignment-misuse problem!
compare(0, X) // no assignment symbol even in use!

This shows the real scale of the problem. Inverting expressions, which makes them little understandable for people thinking standard ways, are used everywhere where such expressions are in use (not just those involving == and != operators). And in result, you have maybe 15% cases of assignment misuse prevention, with additional 5% of actually causing the risk of assignment misuse where it didn’t occur, and remaining 80% are simply useless, of which only 10%p were those using == and != operators.

The question is then, why can’t they simply focus exclusively on expressions where you really have a variable expression on the left and constant one on the right, and leave all others alone. They will then respond simply: because they got used to it by this rule.

Focus on this: THEY GOT USED TO IT.

To what? That the variable should not be on the left? No. To that they should invert the order of TESTED OP PATTERN and make it PATTERN OP TESTED. No matter that the rule of assignment misuse prevention speaks nothing of this. This “order inversion” is being remembered as the correct procedure because for human brain it is easier to understand, remember, and use in practice, and for the first look it seems to be the good enough approximation and can be though of as a functional equivalence.

Simplification is probably not the only reason. Another one I could see here is because changing the order is seen by the programmers as a kind of challenge (you need to “invert your thinking”, which is some hardship that you have to push over), and once you have “mastered” it you feel with it like you have learned a new skill. And as you can see a profit in using this skill – because with this you automatically invert expressions like a == 0 into 0 == a – you get some kind of satisfaction of using this “new skill” by “challenging yourself” to think in inverted expressions every single time when you have an opportunity to do it.

4. Bug in your brain

The biggest problem however is that most of the conditional inversion cases are done by programmers who have memorized the inversion so well that they have even started thinking by themselves in the inverted conditional. The reason why they write these expressions inverted automatically is that they no longer think in normal order.

You can argue, of course, what the normal order is. The problem is that the order of PATTERN OP TESTED is the order used normally everywhere, not only in programming, also in daily speech. People doing this inversion do it only for programming, and simply when they have to write a conditional expression, they always think that they should invert the order and the TESTED must always precede PATTERN.

So first, even if one might argue that there’s a big price paid by adhering to the inversion rule (I personally think it still is), which is that in case of cooperation between normal and “inverting” people in one team is hard and results in largely extended working time (as the inversion rule is never explicitly stated and never explicitly required in any formal rules in programmers’ teams, so at best programmers simply do it on their own), the other factors are here important: the fact that this rule applies to all conditional expressions, but only in a few cases really prevents from any problem, and the other one, that it doesn’t even ensure 100% reliability – in short, the wrong or useless transformation happens in majority of cases.

It wouldn’t be even a big problem, if this rule of assignment misuse prevention was really used literally, so only those expression that are affected are inverted, while all others, where inversion would be useless, remain as they were. At least they would be minor in the code and therefore bearable. But if ALL expressions are inverted, the whole code becomes harder to analyze for those, who don’t have this “bug in their brain”, and the other way around – if someone doesn’t apply the inversion rule and doesn’t need it, for those with the bug this “normal” code is harder to analyze. Effectively then, programmers doing inversion and those not doing it cannot work together in one team, or their common efficiency suffers.

5. All for naught!

Well, guys. The statement that the misuse of operator = may lead to undetected errors might have been true a long time ago, but not with today compilers, as long as you seriously treat warnings and turn them on. There are easy methods as to how to prevent the assignment operator to be used mistakenly where comparison was meant. Three basic rules.

5.1. Turn on warnings and treat them seriously

Remember: warnings are meant to save you trouble. Good compilers have warnings so well designed that you can always replace given expression that generates a warning with an alternative, more explicit expression, which will be explicit enough so that the compiler doesn’t have to warn. Example for gcc:

if (a = nextValue(c)) // Warning: assignment in conditional
if ( (a = nextValue(c)) ) // ok, double parentheses are explicit

The rule is then: always use warnings turned on – maybe some well selected warning options can be good for particular compiler – and those that are reported should be treated seriously and the code should be changed so that they are no longer reported.

This warning, however, isn’t reported in case of C++ defined operator = for classes – but read on.

This rule for interpreted languages, which are not being compiled before deployment – such as JavaScript – can be changed to “always use lint tools and treat the reported problems seriously”.

5.2. Make a code standard rule to avoid assignments in conditions

Every expression like this:

if ( (a = nextValue(c)) )

can be also written as:

a = nextValue(c);
if (a)

So don’t try to be a hypsta. Write the code decently and you won’t cause problems. Even expressions that use the while loop, like this:

while (int x = get(id, value))
{

can be also considered to be written:

for (;;)
{
    int x = get(id, value);
    if (!x)
        break;

And no, this isn’t any more dangerous than the first one. Dangerous is a loop that doesn’t have breaking condition or a loop that doesn’t test all breakable conditions as it should. But it doesn’t matter in which place you are testing this condition. It’s even better if you simply avoid at all any conditions that made side effects being used in the while conditional expression.

It doesn’t prevent a mistaken = to disappear if you simply don’t use them intentionally, as mistakes can always happen. But if you stick to the rule that an assignment expression used as a part of another expression is never normal or tolerable thing, you will more probably catch this thing during a code review or when you analyze it.

5.3. In C++ classes, make operator = return void

You read in any C++ course book that your class in the assignment operator shall return the reference to the assigned object?

Then that’s wrong. Class-defined operator = shall always return void. And I mean always.

There is some residual usefulness of returning the reference to enable expressions like

a = b = c

But please, you can also split this into single expressions, and the gain in the form of that this expression will generate a compile error is more important:

if (a = b)

If a belongs to a class that has defined operator= and it returns void, this expression will simply make that the type of the expression is void and therefore not convertible to bool. And this will generate a compile error.

There’s generally a good rule to avoid using expressions that generate side effects. By doing this you simply increase risks of getting an undetectable error.

6. Conclusions

Forget it. Simply. And think of the above experience with assignment misuse prevention before considering it next time. Also when someone insists, you can show them this.


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4