if (systemcall(“some string”, expression_argument(args), SC_MODE_1 | SC_MODE_DEFAULT) != 0)
if (0 != systemcall(“some string”, expression_argument(args), SC_MODE_1 | SC_MODE_DEFAULT))
One may find it easier to read/navigate flow control in C code that returns status codes, when these codes are stated beforehand. When there are series of long lines and a mix of 0==success and 0==false, it is easy to get lost, at least in my experience.
Until you repeat that code pattern 10 times in one function. Then, do you reuse the same result variable? Or have 10 result variables each with a different name? Or put the whole thing in a block to limit that variable to the scope of that block?
In the past, I've done things like this:
#define TRY(exp) \
do { \
int TRY_result = exp; \
if (TRY_result != 0) \
return TRY_result; \
} while (0)
(Sometimes, one wants more complex error detection than just comparison to zero, or more complex error handling than just returning the result code. Often, it is possible to build a more complex version of the above TRY macro to meet those specific requirements.)
I would agree with your style, unless all monitors and our vision were built to be more ultrawide-aware than “ultrahigh”. Irregardless of resolution, we can have at most 50-70 lines per eyesight and I doubt that constraint cannot be left unconsidered without losing readability. But it is just my guess from observations, not a real science.
Did you compare writing in different ways and reading it later? This specific style confuses me the most, because I have to mentally connect an assignment with a flow control, because there is no guarantee that a following if() will not use a different variable or it was not shadowed, misnamed, etc. And given that, at once I see x2-3 lines less than usual.
You either grow it to the right, or in a vertical direction. If the latter, conditions also disperse across that axis. What matters more in a specific case is up to the writer to decide who tries to remain both readable and concise. Highlighting important details in a homogeneous text is part of “the art”.
> Leave a comment there explaining the assignment.
Please don't. This is a perfectly understandable idiom in C. Don't explain the language in your comments; that's the job of a text book, which should be read by anyone before they read your code.
I've been going with this style for things like reading files (with retries) or any sort of loop that feels awkward.
while (1) {
int ret = ...;
if (ret == ...) break;
if (ret == some_other_condition) break;
// additional termination conditions....
// do exactly one thing
}
Which is great if your project treats warnings as errors. Not sure how common this is though since every time I compile a C program it’s just pages and pages of warnings.
Exactly. All code I write must pass -Wall -Werror -Wextra and there's always a debugging phase once you have written the code since there are syntax errors and whatnot, a lot.
A useful aspect of yoda notation is that it often makes an equality test easier to read because the constant is shorter than the expression it is tested against, and the == sign comes right away, not buried far away in the line.
Not all languages allows chained assignment (a=b=c) like c++ does, which is why we have this problem (and probably does not automatically cast them to bool either)
It is part of the reason. In c++ a=b returns a reference to a to allow chain assignments. And also in c++, most stuff casts to bool automatically when you use them as an if condition. When you have both and make the typo, it gives you a relatively silent bug instead of a compile error. Yea sure, the reason is a typo and easily mistakable operators but the actual reason is the code compiles fine
Java for example expects a bool for for if(). if(a=b) is only valid when they are, which is relatively rare. So it is mostly a compile error.
Yeah, valid point. Those other things are factors.
If C didn't allow assignments to be used as expressions, then it wouldn't be an issue.
Chained assignment isn't the only way to use an assignment within an expression, so I think it's still more accurate not to say chained assignment as the cause, but chained assignment might have been a big part of the motion for making assignments expressions instead of just statements.
I know I'm in the minority, but I honestly never really understood why people get so up in arms about this. People keep saying it's so difficult to read, and I just don't understand why. I know that linters and such can catch this most of the time, but a lot of places I've seen don't use them. (I don't know why, that's a whole other discussion.) It just seems to me that it's something that keeps you from wasting time on silly mistakes should be common place. I really don't understand.
You must be young, friend. The holy wars that used to rage over just where to put curly brackets...
(And then Python came along and was like, "U wot mate?", and there were had little wars over "syntactically-significant indentation" and tabs vs. spaces, and how many spaces... And on, and on...)
Oh no, I'm not young. I just find this particular argument makes less sense than all the other types of arguments like this. Bracket placement in some languages is necessary to not have unintended effect. Tabs vs spaces can cause problems with certain ides/editors. I can somewhat understand most justifications as to why people have a certain reason for formatting code in their particular way. You want me to indent 3 spaces instead of 2 in Delphi, because that's the way you've done it, fine. I don't care. But this particular one, I don't get it. I never have. The amount of time that this one particular trick saved me in terms of time and frustration is huge. When you spend 45 minutes tracking down a problem because you assigned 5 (or something) to variable by accident, it almost always leads to a mouse throwing event. When someone comes back and says that seeing 42 == x is too difficult to read even though it can save countless hours, I just don't understand. (not every project has the benefit of having a linter or a compiler that will catch things like this.)
I see what you're saying, in this case there's real benefit to doing it one way rather than the other. But if that was enough to get people to change we would all be using better languages and tools, eh?
I got lucky I think. The first time I saw "yoda" conditions I had an abreaction. But then I realized the programmer who wrote it was "a foreigner, with ways different than our own." That somehow made it alright again.
Virginia Satir said that people would choose the familiar even over death. She said the strongest human drive is for the familiar.
Jef Raskin pointed out that the way we use the word "intuitive" it really means "familiar". The first time he handed a mouse to someone to try, who hadn't previously seen it in use, she turned it over and used it like a little trackball.
I'm pretty sure the only reason we don't all use Lisp is just human drive for what's already familiar.
Code is for humans. If your language lets you make silly mistakes maybe it needs to stop doing that. Why can I assign during a branch? It's unnecessary.
Are they really easier to read though? A lot of the time which variable being tested is obvious, so it makes sense to put the important information - the value - first.
Despite the name, it isn't referring to Yoda's grammar form, it's just reversing things around "and" and "or". If you really have a problem telling that "x == y" is the same as "y == x" I suggest coming to a deeper, more complete understanding of the equality operator as a commutative operator where the order doesn't matter rather than thinking of it as "variable is? value", as so many students clearly pick up accidentally.
I've actually noticed one thing about Common Core is that they do try much harder than when I was a kid to not accidentally create that impression; my kid's homework is full of "4 + 3 = ___" followed immediately by "____ = 8 + 2". Still kind of glossing over "=" as a "simplify" operator, but it's still an improvement over when I was a kid when it was really easy to pick up the idea that the "=" operator was actually a function meaning "take the expression on the left and simplify it". (I mean, there's a sense where such students aren't even wrong; it is the rational conclusion from the evidence presented.)
> If you really have a problem telling that "x == y" is the same as "y == x" I suggest coming to a deeper, more complete understanding of the equality operator as a commutative operator
Code is for humans, not computers. The order I choose when writing an expression is driven by what I want to convey to the next person reading my code.
`if (button.state == .enabled)` suggests to the reader that the button’s state is the thing we’re interested in in this particular piece of code. Reversing the operands is confusing not because I don’t understand the communicative property, it’s confusing because it puts the emphasis on the wrong thing: I’m not checking if the enabled state matches an expectation, I’m checking if the button’s state matches an expectation. And I want the reader to understand that.
The first sentence uses active voice - the second passive. Active voice ascribes agency to X; It makes a subject/object distinction between X and Y. Passive voice makes them both objects.
When reading code, it makes more sense to ascribe agency to variables than to constants, which is why people are more comfortable reading x == 0 than 0 == x. Zero can’t change, so it can’t do anything to make itself equal something. X can change, so it is capable of equaling things.
If you’re equally comfortable with either formulation, maybe you just prefer the passive reading of the sentence.
But I do wonder if you’d be equally happy if I went through your codebase and replaced every for loop condition from i < length to length > i...
This is exactly the point I'm making; if we must ascribe agency at all (I'm thinking that's a category error here, but human thought is fuzzy and I'll roll with it for now), it would belong to the "==". Don't read it as English at all; read it natively. There is a reason we call these programming languages, no joke, no pun.
"But I do wonder if you’d be equally happy if I went through your codebase and replaced every for loop condition from i < length to length > i..."
You'd have a hard time of it; I'm a vigorous believer in doing whatever you can in the language you're in to avoid the old 3-element-style C-style for loops in favor of "for ... in ..." or local equivalent. I'm pretty sure you'd get single digit hits for C-style for loops with multiple clauses in the condition.
> Still kind of glossing over "=" as a "simplify" operator, but it's still an improvement over when I was a kid when it was really easy to pick up the idea that the "=" operator was actually a function meaning "take the expression on the left and simplify it".
What? No. De '=' sign is very simple: what's written to the left of it is equal to what's written on its right.
The takeaway that many students have is that the equals sign if equivalent to the -> sign, which definitely implies simplification. I know I was set straight as a university freshman in the 1980s, and I have read many things since about that same confusion.
Outside of math it probably doesn't matter. Inside of math it matters a lot. Programming is math.
I just want to confirm this is what I meant. While I wasn't as badly affected as some of my peers, I still remember when I truly realized what equality meant. That occurred in college, and embarrassingly late in college at that (though it was in undergrad at least). I suspect I got some extra cognitive interference from my programming language experience, so non-programmers would hopefully have an easier time of it.
And I was solving systems of equations with substitution and doing all the usual things to equations one does in math classes, and to all external appearances I would have looked like I understood equality prior to that. But I didn't. Not fully. In hindsight, the best evidence of this was in physics class, where I was perfectly adequate at taking the provided equations and using them, and I understood their derivation, but I still wasn't all that great at combining them fluidly; F = ma, yes, but it still didn't quite fully register that that meant anywhere I saw an F, in any other equation in which it appeared, I could drop in an ma. It's not that F is "convertable" to ma, or that ma simplifies to F, or that the name of ma is F... it's that they ARE each other, there is no conceivable way you can separate them, there is no conceivably witnessable in any way, mathematically or practically, effect from substituting one for the other, there is only one entity that can be named in various ways. I did not fluidly combine them the way I should. If I had the class to do over again, I would do way better this time, even we could somehow erase my first pass entirely from my mind.
So I'm writing a unit test. I bet everyone here can correctly guess the language.
assert ('Content-Type', 'text/plain') in dupefail.headers
assert b"registration denied" in dupefail.body
assert "403 Forbidden" == dupefail.status
I also happen to pay attention to the linter fart^Woutput:
C: 61,11: Comparison should be dupefail.status == '403 Forbidden' (misplaced-comparison-constant)
I totally admit my thorough hate of pylint, it hasn't really ever helped me once -- mostly led to more `#pylint: disable=…` garbage in the source. I still force myself to use it, as a duty by fellow... other engineers. But that's an aside.
Please, tell me how much more "natural" and "un-ugly" the 3-line snippet would read to you had it the third line assert flipped away from Yoda style, just as pylint suggests. I'm eager to hear you.
Look, I'm trying to humanly argue against the statement "Yoda conditions are unnatural". Nature has no boolean conditions. That statement should instead say "Yoda conditions are unfamiliar (to me)" -- at which point it's way easier to see the statement's applicability limits, and dramatically narrow down its consequences.
On the contrary; there're many common contexts where Yoda comparisons looks more "natural", meaning they avoid breaking the surrounding code flow, and bring the important part (the constant) up-front. I even brought up a real-world example. Having read `assert "403 Forbidden" == ` and remembering the context, can't you already guess the RHS (and just skim over it)? Sure you can. Non-yoda loses here.
Be aware: you don't have to take a "for/against" side in this debate, as our buggy brains try to in every flame war. Both sides have a point. Familiarize yourself, and decide on case-by-case basis.
While we're at it: self.assertEqual() is super-ugly and unnatural, in my judgement. Why am I forced to use _thrice_ as much words to express the simple single-word concept of an assert? Why can't I spare the extra pair of parens, and spell == directly? I see nothing wrong with bytecode rewriting; it's amazing they can do it, and I appreciate the effort.
Treating the cause's cause: use a programming language which makes a distinction between actions and values, so that an action-which-produces-a-boolean is not valid where a boolean is expected.
Treating the cause's cause's cause: design your own programming language to be a strongly typed functional or logic programming language that is a descendant of the ideas in XSLT, where the attitude was “the most common thing you do in programming is to map one data structure into another, so let’s build the whole language around making those mappings easy first.” There can now be no confusion.
Probably treating the cause's cause's cause's cause is something like “just use Excel for everything, why are you using these other languages?”
Alternatively there are languages, where this problem can't arise at all.
In Clojure for example: (= ...) just tests for (actual) equality, while (def ...) assigns a symbol to a value, or (swap! atom f) changes the value of an atom (reference type).
I once worked for a place that explicitly told me not to do this. They also told me to test booleans with
if( booleanVariable == true ) ...
This was also the place that ordered me not to use LINQ statements because, and I quote, "you need to write code that someone fresh out of high school could understand". I don't work there any more.
We actually have to write boolean checks like that in Kotlin nowadays in modern Android development. If your variable type is Boolean? instead of Boolean, it is needed to both check if it is non-null and true.
only tangentially related, but one of the things I miss so much about Perl is that, as in English, you can add an "if" or "unless" clause as an afterthought.
$x++ if (!condition);
like if you start to increment $x++ and then you realize wait a second I only mean if....
"unless" likewise functions as an "if not".
This is such a "natural" way to write.
Let's compare what happens if you have the thought to add a "statement modifier" in any other programmer language.
you're written your statement, and now you have to go back to the beginning of your line, write your condition, open a brace, go back to the end of the line, and close your brace. Or maybe begin an entire code block, since you can't have it on one line anymore.
it is so much less natural. why can't other programming languages have that?
Ruby has that. But you have to be careful that your code stays readable. I know that's not a concern for Perl. :-)
Although the "unless" case very often trips me. Somehow my brain can't parse that correctly and I've found that surprisingly many work colleagues have the same problem.
<result> if/unless <condition> is pretty common in natural language for good reason. I find it far more readable than prefix, block-form if for the simple case of one-line results with no alternative branch.,
It should be noted that Yoda conditions originated at a time when compilers didn't warn about accidental assignment, and linters weren't that great or had poor compatibility.
More often than not, the first solution to a problem isn't the best, but the best solution would take a lot of tooling fixes.
The Yoda condition trick is a relic of the past, well past its usefulness and best left in its grave alongside Hungarian notation, macros for "inlining", #include "blah.c", goto, etc.
In Java at least, you almost can't get away from Pokemoning because of the split between checked and unchecked exceptions. About half of libraries throw checked, and the other half throw unchecked.
Or even use plain old := for assignment to help avoid confusion with ==. I wonder if K & R wanted to save a character for terseness. I mean, these are the guys who said, if they were writing Unix again, would "spell creat with an e."
Yeah. when hacking C and C++ into this habit I got. It was hard to break when using other languages.
I've had a lot of feedback from colleagues that it's harder to read because it's rare. If my colleagues say it's hard to read, that's good enough for me.
I use Yoda style string comparations in java all the time since they are null safe. But other than that I wouldn't have much use for them because they're more difficult to read
They're required per the WordPress PHP Coding Standards and flagged by the WordPress rules for PHPLint. Lots of WordPress developers stick to those standards, so that's why you see it everywhere.
There are zero reasons to use that ugly and unnatural Yoda notation in 2019.