10 ms·
Ifs and &&s and Plan 9's Source Code
- jacobparker 13y agoC++ has "and"/"or"/"not" etc., so you can do if(not A and B) but I'm not sure I've ever seen anyone do it. http://en.cppreference.com/w/cpp/language/operator_alternative http://en.cppreference.com/w/cpp/language/operator_alternati... To get them in C, see the note about iso646.h in the link.
- kostya-kow 13y agoI don't think it really improves readability. All the programmers are familiar with && and ||, and using something else will just confuse people. Also, it's not really that hard to #define it yourself, instead of using includes.
- jacobparker 13y agoI definitely wouldn't use them because they are unfamiliar. However, in my head I pronounce "&&" as "and" and "&" as "bitand", so using and/bitand over &&/& would reduce a depressingly not-uncommon typo for me. However, on balance, not worth it to me. Note that in C++ they are keywords which is superior to macros.
- kostya-kow 13y agoHow are they superior?
- mikeash 13y agoFor one, a macro "and" would prevent you from using "and" as the name of a struct field, while a keyword will not. Error reporting will be better too. The differences don't seem all that big though.
- orangeduck 13y agoAs an idiom I quite like this form. I often do similar for double iterations: for (int x = 0; x < width; x++) for (int y = 0; y < height; y++) { do_something(x, y); }
- deleted 13y ago[deleted]
- slg 13y agoThis also provides an advantage when debugging. It will become immediately obvious which condition fails when stepping through the code. That isn't always the case with a long string of &&s.
- Groxx 13y ago^ this is a detail which boggles my mind. why are our debuggers still line based? they're clearly not in every respect since you can basically always 'step in'to an && sub-expression, but nothing displays progress as you step through such things, nothing lets you put a breakpoint at some sub-expression, nearly every feature of every tool is delineated by lines as if they're the important part of a program.
- darrenkopp 13y agoWith visual studio and c# you can put a break point at a certain point in many expressions and while in the debugger you can have it evaluate expressions individually when hovering with the mouse. Stack traces, however, are still line based and thus if an exception occurs (like null reference) you only get the line, not the statement.
- rayiner 13y agoBecause something you do relatively early in the compiler is throw away all of the structure of the source, including flattening nested expressions into a linear IR. Mapping back to line numbers in the debugger is a bit hacky to begin with, and mapping back in an even more fine-grained way would be more complex still.
- Groxx 13y agoTrue (currently, I see no reason this is a necessary step), but that suggests you could perform trivial expansion of lines like `if (a.what() && b == c && (d == f || d < 5))` into multiple lines like you see in this article, and then use the exact same hack to get those pseudo-lines into the final stages, and into your debugger. You could even explode each piece into extra variables, so you can see the results of `a.what()` without re-evaluating it. Honestly, even if you had to hit an 'expand this statement' button in your debugger to see `if x() && y()` spread into: x_val = x() if x_val y_val = y() if y_val ... it would completely remove the necessity to write strange things to get around this limitation. Why do we have compilers and a huge variety of languages if not to stop writing strange things unnecessarily?
- JoshTriplett 13y agoIf not for the need to get and later free the Dir *b; , and print the message, this condition would get a lot simpler. I'd find it more readable to write a function bool samefile(Dir *a, Dir *b) , have it consist of a single-line return statement, and then have the caller just do if (samefile(a, b)) { ... }.
- jamesaguilar 13y agoGood luck using an auto formatter on a code base that uses this technique.
- adrianmsmith 13y agoI believe that the layout of code is done by humans for humans. An auto code formatter destroys all of that meaning. I blogged about this more here: http://www.databasesandlife.com/do-not-use-automatic-code-reformatting/ http://www.databasesandlife.com/do-not-use-automatic-code-re...
- jamesaguilar 13y agoYeah, I saw that. I think it's misguided. The amount of time spent on arguing about style far exceeds the time savings of the changes you propose. People can get used to different formats quite easily, but time spent arguing about and maintaining formats can never be recovered. Also, your formatter looks pretty brain-dead. clang-format formats all of those cases (in C++) as in your green examples with Google style turned on. Even the Eclipse formatter should do a better job than what you've posted with the appropriate settings. When I advocate an auto-formatter, it doesn't make sense for you to respond with a blog post talking about a formatter seemingly designed to make everything worse.
- LeonidasXIV 13y agoI agree and for this reason, gofmt, an automatic formatter for Go code is possibly my favorite part of the toolchain. It is kinda like PEP8 for Python but formalized into a program (there is a third party pep8 program) or Emacs for Common Lisp. The advantage is that it is a standard tool. So I can just run it over my code, no matter what editor I used and the code is fine. No arguing what is right, no thinking about where to put some stupid spaces or how to break a fucking line, just run gofmt, done.
- adrianmsmith 13y ago"People can get used to different formats quite easily, but time spent arguing about and maintaining formats can never be recovered" This is nevertheless what this whole discussion is about :-) e.g. should one list those ifs straight like that, or use &&, etc. "Also, your formatter looks pretty brain-dead", it is, alas, what Eclipse does, and that's pretty standard in the Java world, and it's also pretty standard to use its code formatter alas.
- ernesth 13y agoWhen you use the fact that && is not the logical conjunction, you'd better use another if. if (b != nil) if (b->bla == a->bla) is so much clearer (and language agnostic) than if (b != nil && b->bla == a->bla)
- rowborg 13y agoOne downside to non-braced conditionals is that a semicolon accidentally placed after the conditional will cause the block to always run, e.g.: if (null != foo); bar(); This is valid code in C and Java, and bar() will always run in this case. Having seen people waste hours on such a semicolon, I always use braces, even in one-liners, because I never know when someone is going to break it out into multiple lines later: if (null != foo) { bar(); }
- wikwocket 13y agoI was going to say that a compiler should issue a warning for this, as you'd almost never want a semicolon right after an if condition, but to my surprise Eclipse doesn't seem to flag it. It does however indent the line after the semicolon to the same level as the if, which is at least a red flag that something is up, if you are used to how the auto-indenting normally works.
- rowborg 13y agoAbsolutely, indentation helps catch this when writing. When I've seen this happen, it wasn't because of a semicolon added when the code was written. It was someone accidentally adding a semicolon to a line later, without realizing it. Unless they then went to the next line and hit the "fix indentation" key, they didn't catch it.
- comex 13y agoBoth clang and gcc warn on "if(1);", clang by default, gcc with -Wextra. clang also warns on "while(1);" - I think this is somewhat obnoxious, since it can be useful, and would prefer if it only warned if the semicolon was followed by an opening brace, but YMMV.
- thedufer 13y agoI don't think braces solve that particular problem: if (null != foo); { bar(); }
- pavanky 13y ago
- jbverschoor 13y agoI often use: if (false) { } else if (something) { } else if (something) { } To get some indentations right. When constructing UI elements in code, I used to scope copy/pasted blocks: { Button button = xxx; // and some more } { Button button = xxx; // and some more but different } In the rubydays it'd be: Button.new do |b| something end
- triztian 13y agoHow about using this alternative form: if((b != nil) &&(b->qid.type==a->qid.type) &&(b->qid.path==a->qid.path) &&(b->qid.vers==a->qid.vers) &&(b->dev==a->dev) &&(b->type==a->type)){ fprint(2, "cp: %s and %s are the same file\n", an, bn); ret = 1; } It keeps almost the same visual look and it uses the common convetion, except for the && at the beginning of each line.
- phoboslab 13y agoI still prefer this: if( b != nil && b->qid.type == a->qid.type && b->qid.path == a->qid.path && b->qid.vers == a->qid.vers && b->dev == a->dev && b->type == a->type ) { fprint(2, "cp: %s and %s are the same file\n", an, bn); ret = 1; }
- IvyMike 13y agoWe can go deeper... if( b != nil && b->qid.type == a->qid.type && b->qid.path == a->qid.path && b->qid.vers == a->qid.vers && b->dev == a->dev && b->type == a->type ) { fprint(2, "cp: %s and %s are the same file\n", an, bn); ret = 1; } (Lined up the "a"s to make it obvious that they're all the same.)
- deleted 13y ago[deleted]
- MindTwister 13y agoI'm not sure if you're trolling or not... but isn't that the point of the function in the first place...?
- MaulingMonkey 13y agoI'd prefer some variation on: int samedirfile( Dir *a, Dir *b ) { if( a == b ) return 1; return ( a && b ) && ( a->qid.type == b->qid.type ) && ( a->qid.path == b->qid.path ) && ( a->qid.vers == b->qid.vers ) && ( a->dev == b->dev ) && ( a->type == b->type ); } ... if( samedirfile( a, b ) ) { fprint(2, "cp: %s and %s are the same file\n", an, bn); ret = 1; }
- kostya-kow 13y agoIt looks weird without tabbing. But if the code was tabbed, then it would take too much space, since they are using huge tabs. That's why I prefer 3-space tabs. Not too small, not too big.
- zalew 13y ago3 tabs? is there a language, community or editor that encourages this convention? usually the default for tab is 4 or 2, and 4 is the PEP convention for Python, while 2 is very commonly used around Ruby as far as I have seen. btw there are no tabs specifically because it's meant to be read as a single statement, not nested conditions.
- dllthomas 13y agoFollowing typical layout rules, there's a much easier way to get this conjunction without indents: if(foo == bar && baz == qux && ! frob && whatever() && something) {
- adrusi 13y agoI use this convention often for loops: for (int x = 0; x < width; x++) for (int y = 0; y < width; y++) if ((x + y) % 3) { // ... } The semantics are kind of like using a comprehension.
- cnvogel 13y agoI abhor deep indents, and when I have deeply nested loops that just serve to trivially enumerate things, in C I sometimes add a helper function for the iteration. e.g. instead of: for(int i=0;i<N;i++){ for(int j=0;j<K;j++){ for(int k=0;k<L;k++){ ... it might become i=j=k=0; while(all_frob_indices(&i,&j,&k)){ .... with all_frob_indices() doing the i++; if(i>N){ i=0; j++ }... This makes code look more similar to e.g. itertools-constructs in python where you can easily make products, zips, ... from iterables.
- barrkel 13y agoDoing this stops most compilers putting the index variables in registers, with knock on effects on array indexing efficiency. Not necessarily a bad practice, but something to be aware of.
- justin66 13y agoIt really is a bad idea, though, for the other obvious reason. It goes from being something any programmer can figure out immediately to something that requires additional thought to understand. The only upside is that it satisfies someone's indentaphobia. No, thank you.
- a-nikolaev 13y agoNot really a bad idea, imo. The upside is that you cannot make a stupid typo in these nested loops, which is really easy to do, when you have to write the same thing several times in several places of your program. Also, it is easier to refactor, when the need will be.
- 13y ago
- mixedbit 13y agoAs a side note: if(b->qid.path==a->qid.path) is interesting. This is not how you compare strings in C. So it is either a bug or dirstat() needs to guarantee that different results pointing to the same file always share the path string.
- jedbrown 13y agoThis line is comparing integers, not strings. Qid is a structure containing path and vers fields: path is guaranteed to be unique among all path names cur- rently on the file server, and vers changes each time the file is modified. The path is a long long (64 bits, vlong) and the vers is an unsigned long (32 bits, ulong). Thus, if two files have the same type, dev, and qid they are the same file. http://man.cat-v.org/plan_9/2/stat http://man.cat-v.org/plan_9/2/stat
- deleted 13y ago[deleted]
- hcarvalhoalves 13y agoAmazing how old C, and still obscure things always pop up here in HN. I had no idea of such idiom.
- ebbv 13y agoOh god that code is fucking hideous. If someone who worked with me wrote that I'd talk to them about it and make sure they never did anything like that ever again. From the terrible argument names to the abuse of the single line if syntax (which should never be used anyway, always use curly braces.)
- orangethirty 13y agoThats hideous? I see worse code everyday.
- ebbv 13y agoJust because there's lots of worse code doesn't make that good. There's worse songs than "Who Let the Dogs Out" but it's still a really bad song.
- cheald 13y agoYou would love this: https://feralbynight.googlecode.com/files/FeralbyNightv3_2_b_beta.cpp https://feralbynight.googlecode.com/files/FeralbyNightv3_2_b...
- danbruc 13y ago11k lines in one file - this is the way it should be done. ^^
- encoderer 13y agoMaybe it comes from using Python as my go-to language (and that it's my favorite language) but i personally like to avoid non-essential braces and other minutia. Yes, of course, I know the argument: a one-line bracketless "if" can set-up a future developer for failure if they need to add an item to the conditional block. And if they for some reason decide not to read the actual conditional. And if they don't test it. And I don't hate code that uses braces even when they're not strictly needed, but I personally prefer to omit them. And my feeling is that the dogma around braceless-ifs is a little overblown. Of course when working on a team that has adopted a no-braceless-if policy, I conform. Having consistent code is way, way more important than somebodies own favorite bracing style.
- pieguy 13y agoAnd what's the alternative for if(A && B){ x(); }else{ y(); }? Seems very easy to shoot yourself in the foot with this approach.
- anonymous 13y agoWell, you could if (one) { if (two) if (three) if (soon) { stuff }} else { else stuff } Personally, I think I won't have any trouble reading that, but I have noticed I'm somewhat more tolerant than others in this regard. Though I am more OCD than others in other ways.
- ernesth 13y agoHere, the x() and y() are assignments, so it's easy: ret = 0; //y() if (A) if (B) ret = 1; //x() If the else statement is more involved, it gets difficult.
- danabramov 13y agoThere is a similar [common][1] idiom with `using` in C#. When you need to create and later dispose several resources, to avoid excessive nesting you write using (var outFile = new StreamReader(outputFile.OpenRead())) using (var expFile = new StreamReader(expectedFile.OpenRead())) { ///... } [1]: http://stackoverflow.com/a/1329765/458193 http://stackoverflow.com/a/1329765/458193
- OldSchool 13y agoPerhaps the compiler wasn't relied upon to provide "short circuit" boolean eval? This code will compile that way no matter if it's available in the compiler or not. Honestly for the sake of being more robust, I'd add if(a != nil) after the first test of b. if(b != nil) if(a != nil) if ...
- SoftwareMaven 13y agoIf the compiler can't do short-circuit evaluation, the compiler can't do C compilation.
- glurgh 13y agoShort circuit is guaranteed by the language standard. On top of that, plan9 comes with its own C compiler so the people who wrote this also wrote the compiler used to compile it.
- OldSchool 13y agoI see, I just have no idea how complete the Plan 9 tools are. Working with in-house domain specific language interpreters or compilers I've twice seen unary minus support missing so I assumed anything was possible when something's not fully commercialized.
- glurgh 13y agoThis popped up in some other thread recently. It was (still is, I suppose) a standard ANSI C89 compiler with a couple of small extensions and tweaks. Ken Thompson's short overview is here - http://plan9.bell-labs.com/sys/doc/compiler.html http://plan9.bell-labs.com/sys/doc/compiler.html You can nerdmuse yourself a little by googling why it had a #pragma hjdicks
- deleted 13y ago[deleted]
- davewicket 13y agoint ret; ret = 0; Why?
- endgame 13y agoThis isn't a new thing. I remember it being used in ZZT-OOP, where you didn't have boolean operators. #if foo #if bar #send obj:dosomething
- carlob 13y agoIn Mathematica the FullForm of a && b is And[a, b]. You can use this to format long lists of conditions as And[ a, b ] You can also rely on the fact that logical expressions are always short-circuited, so False && Print["won't be printed"], doesn't print anything. Finally, you can put a bunch of conditions in a list and do And @@ list.
- klez 13y ago> You can also rely on the fact that logical expressions are always short-circuited, so False && Print["won't be printed"], doesn't print anything. This is true for any language I can think of. E.g. in perl open(my $fh, "<", "input.txt") or die "cannot open < input.txt: $!"; is idiomatic
- lnanek2 13y agoHmm, I'm not a fan of an if without braces. It is just begging for someone to come along and stick multiple statements after it expecting them to run when it fires. Heck, maybe even me tired at the end of the day.
- DoubleCluster 13y agoGreat, now add an "else".
- iajrz 13y agoIt's not complex at all. How can it be confusing? I was confused by the author's confusion :-/
- ericbb 13y agoThe Gish source code does that too. https://github.com/blinry/gish https://github.com/blinry/gish
- stormbrew 13y agoIn ruby, the equivalent to a switch (confusingly called case) can take no initial-compare field and basically become a chained-if that lines up nicely (or more nicely than elsifs). I prefer the visual look of it, personally, but it seems that a lot of people find it too confusing. As an example: case when (a == 1) dosomething when (b == 2) dosomethingelse when (c) orthis else awww end
- jjoergensen 13y agoHave some fun unit testing that! ha
- stox 13y agoI hope everyone realizes that this code was probably written by Ken Thompson or Rob Pike.
- insertdisktwo 13y agoMy knee-jerk reaction to this post was that this increases the chance of another developer introducing a dangling else manyfold. http://en.wikipedia.org/wiki/Dangling_else http://en.wikipedia.org/wiki/Dangling_else
- moron4hire 13y agoI think the meaning was fairly obvious at first glance, and it read as a sloppy way of conjoining expressions, where && should be preferred, because it doesn't rely on the arbitrary block rules of if expressions. Imagine for a moment that a stray ";" ends up at the end of one of those if statements. I don't care how, perhaps you just dropped your Warby Parkers on your keyboard or something. Not only is the code now broken, but it still compiles. With &&, it would fail to compile and the error would be caught immediately. The moral of the story is that syntax is your friend, not your enemy. Use syntax as much as possible to catch errors. Especially with strictly-typed languages, you have an incredible tool for automated verification of certain portions of your program. Use it.
- reddiric 13y agoI think the meaning was fairly obvious at first glance, and it read as a fine way of conjoining expressions, where multiple if statements should be preferred, because it doesn't rely on the arbitrary subexpression evaluation ordering rules of binary operators. Imagine for a moment that a character ends up deleted at the end of one of those && expressions. I don't care how, perhaps you just dropped your Warby Parkers on your keyboard or something. Not only is the code now broken, but it still compiles. With if(...)s, it would fail to compile and the error would be caught immediately. The moral of the story is that syntax is your friend, not your enemy. Use syntax as much as possible to catch errors. Especially with strictly-typed languages, you have an incredible tool for automated verification of certain portions of your program. Use it.
- moron4hire 13y agoLazy evaluation is not arbitrary, it's a well established convention that any sane language implements (thus marking the languages that do not use it as completely insane and unworthy of our effort). And also, your reply is technically incorrect, a typographical error or any kind is more likely to be caught in the && situation, and the style of reply makes you an insufferable geek.
- overgard 13y agoI actually quite like that, although usually when I have a large conditional like that I just give it named boolean variables to make it read more like a sentence, IE: bool exists = b != nil; bool sameType = b->qid.type == a->qid.type; bool samePath = b->qid.path == a->qid.path; ... if(exists && sameType && samePath) { } It's also a lot easier to inspect in a debugger, as all the conditions appear in the locals list.
- iso8859-1 13y agoYou could also group related conditions, so that nested if's don't bloat the locals so much: speedIsSlow = dict(downstreamSlow = downstreamSpeed < 200, upstreamSlow = upstreamSpeed < 100) if any(speedIsSlow.values()): connectionIsDead = getPingTime() > 100 if connectionIsDead: print("It's dead, Jim")
- kbruner 13y agoThis just looks like it was written by someone who's done some logic programming.