Hacker News new | past | comments | ask | show | jobs | submit login
Yoda conditions (wikipedia.org)
133 points by tosh 65 days ago | hide | past | web | favorite | 104 comments



All linters and C compilers emit a warning when an assignment is made in a condition.

There are zero reasons to use that ugly and unnatural Yoda notation in 2019.


  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.


I'd break this up by lines, every time.

  int result = systemcall(“some string”, expression_argument(args), SC_MODE_1 | SC_MODE_DEFAULT))
  if (result != 0)
Separation of concerns. Each line does one thing. Making a system call and branching on its result are two separate tasks.


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)
And then instead of the above I can just write:

     TRY(systemcall(“some string”, expression_argument(args), SC_MODE_1 | SC_MODE_DEFAULT)));

(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.)


By that logic, shouldn't you be creating a var for the expression_argument() return?


Indeed I probably would, just didn't want to distract from the central point in that post.


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.


Yeah but that's also just a ridiculously long line which will be hard to read regardless ;)


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”.


Furthermore, when we have a problem, if our solution is to have human beings simply remember to do it a different way, we then have two problems.


I find this quote very powerful. I'm thinking I could re-use it. Is it yours or if it's sourced from somewhere could you share the source?


It seems like an intentional reference to this quote (Jamie Zawinski):

"Some people, when confronted with a problem, think "I know, I'll use regular expressions." Now they have two problems."


LOL That quote is exactly what I was cribbing from. :)


I think this is reasonable readable:

    while ((status = systemcall(...)) != SUCCESS) {
        do something with status;
    }
Leave a comment there explaining the assignment.

Likewise:

    if ((status = systemcall(...)) != SUCCESS) goto error;
Or something like that. I don't understand the hate, just make sure it's obvious what you're doing.


It’s traditional to build these kinds of loops in C, e.g.:

    int c;
    while((c = getopt(argc, argv, argstr)) >= 0) {...}
I’d say it’s acceptable, and Python is adding syntax so it can mix assignments and checks.


> 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.


That's perfectly fine and arguably even better than the alternatives. The `a = b = c = 0;` format is also good.

But enabling it on the language level brings a large amount of risk and complexity just for what amounts to a micro-optimization.


This would achieve the same but is far more readable:

   while(status != SUCCESS) {
      status = syscall(...);
      // do something with status
   }


Only if status is initialised to something other than SUCCESS.


So use a do-while instead.


And this introduces another bug: it'll run the loop body even if syscall fails the first time it's run.


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
  }


Your transformation has introduced a bug: the loop body will run once after success turns false.


Pretty harsh. Imho it's not ugly nor unnatural, just different.

It also reduces time in debugging as you catch it immediately.


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.


This is true, but sadly warnings can be ignored! At my last job we enforced -Wall so we had no warnings on pull requests to prevent this though.


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.

It's my favourite way of writing if statements.


Not all programming languages !


I'm so glad somebody else thinks this! I've always hated that style of notation.


Do compilers for other languages emit the same warning? Because there are fewer and fewer reasons to use C in 2019 as well.


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)


Chained assignment isn't the reason. The similarity of the "=" and "==" operators is.

Specifically, it's both (1) easy to make a typo where you meant "==" but typed "=" and (2) not easy to visually distinguish the two.


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.


Just wait until people realize proportional fonts are actually easier to read than mono-spaced fonts.


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.


I've found it's better to write normal conditions that are easier to read and leave it to your linter to catch unintended assignments.


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.


> so it makes sense to put the important information

Both sides of the equality check are equally important.


I think he should have written "it makes sense to put the non-obvious information first" as it contrasts better with the first part of the sentence


Am I sure pretty any that linter warning a throw can of instead breaking flow the both of reading writing and.


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.


There are two perfectly valid ways of reading

   x == y
aloud as an English statement:

> X equals Y

> X and Y are equal

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.


I don't have a problem with 'equals' being commutative. My programming language does.


Haha it took me a second to realize what you did there


A second to realize what you did there it took me.


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.


That's why normally you do self.assertEqual() instead of assert.

To properly support assert with good error reporting, pytest has to rewrite the byte code. See http://doc.pytest.org/en/latest/assert.html#assert-details


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.


Even with them, I do find it Yoda-like that you are supposed to make the expected value be the first argument


Interestingly enough they are needed when comparing to $null in Powershell

https://rencore.com/blog/powershell-null-comparison/


I was going to post this too. I ran into this a year or so ago and now always put $null on the left of a comparison.


Treating the symptom: Pollute the codebase with warped code.

Treating the cause: Use static analysis.


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?”


> 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?”

It's be harder to get things done, but we'd be more certain about them


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.


I was also not allowed to use the C# ?? null-coalesce operator. So something like

  if( nullableBooleanVariable ?? false )
was also forbidden.


Ahh... The good 'ol technique for avoiding NPEs in Java.


For those who are wondering what this refers to, it is writing code like this:

    "foo".equals(myVar)
instead of writing:

    myVar.equals("foo")
which could can cause NullPointerException if myVar is null.


you've missed a closing " which makes this read differently than intended


Thanks. Fixed now.


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.

  x += 1 if !condition
Better as

  x += 1 unless condition
I use them not as an afterthought but by design. This is the Ruby Style Guide about it https://rubystyle.guide/#if-as-a-modifier


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.


The greatest difficulty with code is not in writing it but in reading it.


<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.,


Agreed. Particularly useful for for constructs like:

  def do_stuff()
    return unless stuff_enabled?

    ...
  end


Forth love if honk then

Be careful what you wish for...


When your only hammer is C, everything looks like a toe.

Is there a shorter way of saying, "the wrong solution to the right problem"?


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.


Style lives on, even after the practical purpose for it is no longer relevant. People keep wearing denim, even if they're not working in a mine.



Not to mention 'Pokémon exceptions' (catch all)...


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.


solution: don't have assignment operator.


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."

https://en.wikiquote.org/wiki/Ken_Thompson


T&R are the originators of Unix and C. Kernighan seems to often unwittingly be given Thompson's achievements!


Or use ML-style reference cells, so that assignment cannot work on two things of the same type.


Now you're thinking with ~portals~ monads.


HN doesn't support markdown strikethrough, but here's the text in unicode:

p̶o̶r̶t̶a̶l̶s̶


That seems like it would cause problems with screenreaders though.


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 liked Pascal's := attribution. I miss it in almost every language.


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


Yoda conditionals are still very popular in the PHP/Wordpress space. I never got used to using them personally.


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.


I'm aware of this.


One example of a large code base that uses this is WordPress (PHP).


Good job duplicating that second paragraph of the linked article.


Argh, sorry I missed that, I skipped over the intro and looked at the examples.

EDIT: to be fair, this sentence was added today [1]. Not sure if it would have been there when I opened the tab earlier.

[1] https://en.wikipedia.org/w/index.php?title=Yoda_conditions&t...


Of course WordPress uses this in their style guide... if modern compilers catch this, it's a waste of time to use this convention.


Thanks for that!




Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact

Search: