points by Stratoscope 6 years ago

What most of these discussions leave out is that you can't just say "use spaces instead of tabs."

You have to say how many spaces.

Is it 2? 3? 4? 8? How many?

If you use spaces, you are committing to say how many will represent one indent. And please take note of how your choice affects the visually impaired programmers from the Reddit thread. Or someone like me who finds monospaced fonts hard to read and proportional fonts much more legible.

But why do you even need to say this? Why should it matter how many spaces represent an indent level?

The only time it really matters is when you not only use indentation, you use column alignment, like this:

  myFunctionThatDoesStuff(someArgument,
                          andThisCalculatedOne(anArgumentThatMeansSomething,
                                               anotherWordyName));

This style is mandated by many coding standards, including Google's C++ standard. If you use an alignment-based style, you had better use spaces, or else you are in the "tabs for indentation, spaces for alignment" muddle.

What would happen if you abandon column alignment and use only indentation:

  myFunctionThatDoesStuff(
      someArgument,
      andThisCalculatedOne(
          anArgumentThatMeansSomething,
          anotherWordyName
      )
  );

Here the parentheses are treated just like curly braces: if you need to line stuff up, don't do it at whatever column things happened to fall on, do it by indentation only. That's how we code curly brace blocks.

Now your code will be just as readable if it's indented with tabs, or with whatever number of spaces you like.

If we coded curly brace blocks the way these standards require us to use parenthesized expressions, our code would end up looking like this:

  if(someBooleanExpression) {someFunctionCall();
                             if(anotherBooleanExpression) {doSomething();
                                                           doAnotherThing();}}

Do you code like that? Do you want to code like that? Then why do you format code like that (if you do) simply because the delimiters happen to be parentheses instead of curly braces?

If you stop using column alignment, and start using indentation instead, so many problems go away. Space indentation (and how many spaces?) no longer has any advantage over tabs.

I sometimes wonder why so many coding standards mandate column alignment. I think it is because people get tired of talking about it and say "we must agree on something, now! And we will stick with it forever."

I've been in these discussions, and what happens is whoever is most "passionate" about their particular style gets their way. And that will usually be the person who is fussiest about these things, the person who loves fiddly column alignment. Why else would such an impractical approach be baked into so many coding standards?

kazinator 6 years ago

Tabs could plausibly work if the editors were smart enough to treat this:

    myFunctionThatDoesStuff(someArgument,
                            andThisCalculatedOne(anArgumentThatMeansSomething,
                                                 anotherWordyName));

as being all at the same indentation level. If this is at indentation level 2 then all these lines should start with two tabs. All whitespace after those tabs should be spaces. Then you can adjust the tab size without spoiling the relative alignment.

The editor has to understand the language fairly well. (In the case of C or C++, it also has to recognize preprocessor lines and treat them specially, as well as certain elements like goto labels that are out of indentation.)

It won't work in Lisps, because in Lisp, nested expressions have their own internal indentation which can be relative to alignment:

  (long-function-name (lambda (arg)
                        (aligned ...)
                        (stuff ...)))

Basically the only thing that could work 100% would be the original typewriter/TTY/terminal concept of movable tab stops. The editor would grok each expression, and then set up custom tab stops for each line individually:

  (long-function-name (lambda (arg)
                        (let ((var ...))
                          (stuff arg arg
  v                   v v v      v
                                 arg))))

The v's indicate the tab stops for the last line, so five tabs are used. The algorithm for discovering these tab stops doesn't seem overly complicated. We shoot a ray upward at column zero. Whatever it intersects with is the anchor line. We then look for the first tab stops in the anchor line by moving the vertical ray. By this process we discover the ( in (long-function-name, and the ( on (lambda. After that, our vertical hit test encounters the (let line, so that yields a tab stop. If that weren't there, then the (arg) would be the next stop.

Basically tabs should intelligently be based on preceding material, as if an invisible helper were setting tab stops on our typewriter.

One problem with this is that any piece of software that has to format the code has to implement the same thing, exactly.