
Ifs and &&s and Plan 9's Source Code - jayferd
http://computationallyendowed.com/blog/2012/12/03/ifs-and-ands-and-plan-9s-source-code.html
======
slg
This 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
^ 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.

~~~
rayiner
Because 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.

~~~
DannyBee
Both line and column information are completely carried through the process,
and put into debug info, these days.

It's not hacky, it's actually completely well structured. Even debugging info
for C/C++ macros is there and done properly.

None of this is new. DWARF2 (or 3 or 4), the standard debugging format for all
non-MS compilers, has been able to do this well since _1993_.

~~~
rayiner
It's hacky in the sense that it's somewhat ad-hoc the manner in which
line/column information makes it through from the source text, through the
intermediate representation and optimizations, into the generated code and
debug information.

In Clang/LLVM, for example, nodes are annotated with debug information using
LLVM's metadata system. However, it is strictly optional for any given
transformation to preserve the metadata on IR nodes. So what makes it through
after optimizations is kind of a "best effort" affair.

~~~
DannyBee
But this is not ad-hoc at all. This is how it is deliberately designed so it
degrades gracefully depending on what optimizations you choose.

Not all compilers even really degrade. GCC does post-hoc variable tracking
(and thus is mostly unaffected by optimization except when values are optiized
out completely). For declarations and statements, the info is always on the
declaration, so it won't be lost.

Both compiler guarantee that at -O0, all debug info will be kept.

------
triztian
How 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
I 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
We 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.)

~~~
MaulingMonkey
I'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;
        }

~~~
tricolon
I think it's fascinating that we prefer styles that are almost opposites:

    
    
        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;
        }

~~~
MaulingMonkey
I'll note I actually have a slight preference for prepended continuation
operators like you have, but I stick to the style used at work for the sake of
my sanity in trying to write consistent code.

------
rowborg
One 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(); }

~~~
thedufer
I don't think braces solve that particular problem:

    
    
        if (null != foo);
        {
          bar();
        }

~~~
jbert
It does if you use the One True Brace Style:

<http://en.wikipedia.org/wiki/Indent_style#Variant:_1TBS>

since your eyes would flag:

    
    
        if (null != foo); {
           bar();
        }
    

as badness.

[edit: correct } to {, ta]

~~~
georgemcbay
As someone who programs in Go a lot these days, that line (I assume you meant
the first curly to be { and not }) looks less obviously wrong than it would
have in the past when I did more C/C++ programming.

Because I've gotten used to Go's support for short assignment in an if, the
semi doesn't look completely out of place there (though the construction here
is not valid Go either).

------
orangeduck
As 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);
       }

------
danabramov
There 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>

------
overgard
I 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
You 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")

------
mixedbit
As 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
This 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>

------
adrusi
I 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
I 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
Doing 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
It 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
Not 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.

------
jbverschoor
I 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

------
JoshTriplett
If 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)) { ... }.

------
moron4hire
I 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
I 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
Lazy 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.

------
jamesaguilar
Good luck using an auto formatter on a code base that uses this technique.

~~~
adrianmsmith
I 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-re...](http://www.databasesandlife.com/do-not-use-automatic-
code-reformatting/)

~~~
jamesaguilar
Yeah, 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
I 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.

------
DoubleCluster
Great, now add an "else".

------
stormbrew
In 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

------
carlob
In 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
> 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

------
jacobparker
C++ 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_alternati...](http://en.cppreference.com/w/cpp/language/operator_alternative)

To get them in C, see the note about iso646.h in the link.

~~~
kostya-kow
I 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
I 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
How are they superior?

~~~
mikeash
For 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.

------
ernesth
When 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)

~~~
piokuc
I've never heard about writing language agnostic code in my life, lol!

------
ebbv
Oh 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.)

~~~
dasil003
Makes me wonder what the quality of the architecture is on your projects is
though. Not that I think coding style is unimportant, but it's all too easy to
criticize yesteryear's code with today's style standards and totally miss the
forest for the trees. Are you doing work that measures up to the ambition of
Plan 9?

~~~
pjmlp
We all know how well that ambition turned out.

~~~
myg204
Yes, (1) commercially it went nowhere, (2) technically it's a complete working
os with original ideas implemented in a masterful way. Not bad I think!

~~~
pjmlp
I can say the same for many other OSs, like BeOS or Native Oberon, just to
name two of them, but in the end it is (1) that counts.

It is like football, it does not matter how brilliant a team plays, but which
one has higher score when the referee signals the end of the game.

~~~
myg204
IMHO, (1) is the not only think that counts in the end. A 'failed' (as in (1))
concept may have a second life in future works. Also, in the case of plan9
some ideas did get traction (UTF-8 for example), some didn't so it's not all
bad. Comparisons are tricky, you can compare an OS to a football team, and
then yes maybe what you say is right. Or you can compare it to a work of art,
then what matters is the influence the works has in shaping the future...

~~~
pjmlp
Yeah, maybe I was a bit too harsh.

It is just that I personally never found Plan9 that interesting, except for
its successor Inferno and Limbo.

For me operating systems that explore designs done with regards to micro
architectures or safe systems programming languages are much more interesting.

So for me Plan9 tends to be just another OS. And yes I have used it.

------
hcarvalhoalves
Amazing how old C, and still obscure things always pop up here in HN. I had no
idea of such idiom.

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

~~~
glurgh
Short 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
I 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
This 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>

You can nerdmuse yourself a little by googling why it had a

    
    
        #pragma hjdicks

------
iajrz
It's not complex at all. How can it be confusing? I was confused by the
author's confusion :-/

------
insertdisktwo
My 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>

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

------
kostya-kow
It 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
3 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.

------
endgame
This 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

------
pieguy
And what's the alternative for if(A && B){ x(); }else{ y(); }? Seems very easy
to shoot yourself in the foot with this approach.

~~~
anonymous
Well, 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.

~~~
thedufer
That doesn't what he's talking about, though. Your else will run anytime `one`
is false, not when `one && two && three && soon` is false.

------
ericbb
The Gish source code does that too.

<https://github.com/blinry/gish>

------
stox
I hope everyone realizes that this code was probably written by Ken Thompson
or Rob Pike.

------
kbruner
This just looks like it was written by someone who's done some logic
programming.

------
jjoergensen
Have some fun unit testing that! ha

