Tabs vs. spaces

2007-05-07 00:06:37 UTC

Jens Alfke wrote a post of coding tips that includes this advice:

Don’t use tab characters in source files!

The world will never come to an agreement on whether a tab character indents 8 spaces or 4, especially on the Mac, where lots of Unix tools (and Unix source code) are hard-coded for 8. So since different people will have their tab-width preferences set differently, just don’t use tab characters in your source code if you want everyone to be able to read it.

In Xcode, go to the Indentation pref pane and uncheck “Tab key inserts tab, not spaces”. In Textmate, check “Soft Tabs” in the tabs pop-up at the bottom of the editor window. You won’t notice a difference in editing text, but your source code will now look properly indented to everyone.

No, no it won’t. Because now you are forcing your indentation preference on everyone else.

Let’s make one thing clear: tabs have no intrinsic size.. For example, he says “if…you view code that uses 4-char tabs for indentation…”. This is patently wrong: There is no such thing as a 4-char tab. Tabs have no width of their own; they simply say “move to the next tab stop”, and it’s up to the viewer application to determine where the next tab stop is.

Here’s my comment on his post, replying to one of his own comments (which, in turn, was a reply to somebody else’s comment about tabs):

Peter Hosey:

Jens Alfke:

And I find the “wrong” indentation level in files much less annoying than the “wrong” tab width, because the latter makes the indentation completely impossible to follow without reformatting.

But a tab is always the right width, because it’s the viewer who sets it, not the author. Indenting with spaces will look wrong when you move the code to someone who uses more or fewer spaces than you do; tabs don’t have that problem.

The problem comes when you use tabs to create columns. That’s wrong, because then the columns don’t line up when the tab width changes. That, I think, is where your objection originates. You should always use spaces to create columns.

Indentation, however, is the proper use of a tab, and tabs are the proper way to indent.

I’ll take his reply point-by-point:

Jens Alfke:

Peter: It’s not that simple. If the tab width is set to 8, as in all Unix-derived code (and all the Cocoa sources I’ve seen), then the editor uses a mixture of tabs and four spaces to get the 4-character indents.

What?

First off, no source code contains a tab width set to anything*. As I said, tabs have no intrinsic width; it’s your editor/viewer that assigns width to a tab. So, if a tab is 8 characters, it’s because you said so in the viewer application’s prefs.

And if you have set your tab width to 8, then what 4-character indents are you talking about? Are you trying to force such indents despite your setting the tab width to 8?

(There is one exception: If you use a method that has one parameter with a longer name than the first line of the message-statement, Xcode will sacrifice some of the indent in order to colon-align that parameter, resulting in spaces where there would otherwise be a tab. This is a symptom of its colon-alignment logic, which is described below. This only matters in 1%, at most, of Cocoa code, and 0% of other code; as such, this special case should not dictate indentation policy for all other code.)

So that code is going to look completely messed-up to someone with different tab settings.

Maybe so. But if you use nothing but tabs, then it will look perfectly correct to somebody with different tab settings. Let’s say you prefer 4-character indents, and they prefer 2-character indents. Your tab width should be set to 4, and his to 2.

If you indent your code with four spaces, then he will see two of his two-character indents (2×2). But if you use a single tab, then it will still look like four spaces (one indent) to you, and it will look like two spaces (one indent) to him, exactly as he expects.

But if you have tabs set to 8 and you view code that uses 4-char tabs for indentation, the indentation level is 8 characters, which is pretty ridiculous looking and makes most normal code fall off the right edge of the window. So a tab is absolutely _not_ the right width for me.

Here’s what I think you meant:

…if you view code written by somebody who set his tab width to 4 characters, despite preferring 8-character indents, so that he uses two tabs instead of one, then it will look ridiculous.

Which is exactly right, but more the fault of the programmer who does not set his editor’s Preferences to match his preferences than the fault of the tab character. The tab character is innocent in this; it was misused, and that’s what caused the problem.

(Moreover, nearly all code I’ve seen uses extra spaces for indentation. Xcode does this for you in Obj-C code, to make the colons line up. That stuff looks really awful if you change the indentation width.)

Actually, Xcode uses tabs to line up the colons. That’s wrong, and it’s why it looks awful. Xcode should use tabs until it matches the indentation of the start of the statement, and then continue with spaces. For example, if the first line of the statement is indented with two tabs, then every other line of the same statement should also be indented with two tabs, followed by spaces to align the colons. Xcode uses tabs; that’s what causes the messed-up alignment when you change the tab width.


Wrapping it up in a little bow

Here’s an executive summary of the issue. Hopefully this will make things fully clear.

  • Programmers indent their code to indicate scope.
  • For every additional level of scope, there is one indent. This rule is invariant.
  • Different programmers have different preference for the width of an indent. Some prefer four characters; some prefer eight characters; some prefer two characters; and some (crazy people) prefer one character.
  • Some programmers use one tab character (U+0009 HORIZONTAL TAB) per indent. This option is usually referred to as “real tabs”. The programmer defines his preferred indent width in the preferences, and the editor uses that width to define the width of one tab.
    • However, this width is not saved in the file. This is useful, because it means that when you portage the file to another editor where the indent width is different, it will take on the new indent width (e.g. eight characters instead of four) with no modification to the file.
  • Other programmers use one or more space characters (U+0020 SPACE) per indent. This option is sometimes referred to as “soft tabs”. The programmer defines his preferred indent width in the preferences, and the editor indents by inserting that many spaces.
    • This width is saved in the file. Another editor will show the same width, which may not be the width that the other programmer prefers and expects. This is great if you want to force your preferences upon everybody else, but if you would prefer to avoid a formatting war, then everybody should use tabs and set the width that their editor will use for a tab according to their own preferences. Nobody need ever know that other people’s indents are different widths.
  • Thus, tabs are superior because one tab = one indent, regardless of width; it has the right width for you (according to the width you defined in your preferences) and the right width for everybody else (according to the widths they defined in their preferences).

UPDATE 2008-11-29: See also the sequel to this post: Tabs vs. spaces redux.


* I’m aware that UNIX editors like vim and emacs support special meta-text that you can embed that will set the tab width, among other things. I consider this a cheap hack intended for people who do not understand the difference between tab and space indents, or for people who have to deal with those people.

24 Responses to “Tabs vs. spaces”

  1. Martin Says:

    And if you have set your tab width to 8, then what 4-character indents are you talking about? Are you trying to force such indents despite your setting the tab width to 8?

    He might mean the insanity that Emacs uses (by default!) for indentation: An indent is 4 spaces wide; a tab is 8 spaces wide; indentation uses tabs. Which leads to the first level being indented by 4 spaces, the second level by 1 tab, the third by 1 tab and 4 spaces, and so on. Which, to me, is a horrible abuse of tabs and inevitably breaks tab widths other than 8 spaces.

  2. ssp Says:

    I suppose the only way to solve this eternal issue would be to force everybody to use the same tab width setting in their editor. And that just isn’t going to happen.

    Is there a command in XCode that just nicely re-indents everything in a source file?

  3. Peter Hosey Says:

    Martin: I think you’ve called it. That may be the best reason yet to not use emacs.

    ssp: My point is that that isn’t necessary. All that’s needed is for everybody to use tabs—then the question of your tab width becomes unnecessary. Your indent width only matters when you use spaces rather than tabs, because when you use tabs, your indent width is always 1 (except in the pathological case that Martin describes)—there is no variation between tab-using programmers, so there’s no conflict, ever.

    And the command in Xcode to reindent everything is called “Indent Friendly Insert Tab” in Preferences/Key Bindings/Text Key Bindings. It indents all the lines that intersect the selection. I have tab bound to that command, so I just select all the lines I want to smart-indent and hit tab. (To insert a literal tab, use ctrl-q, tab.)

  4. Rainer Brockerhoff Says:

    When Jens said: “if the tab width is set to 8, as in all Unix-derived code (and all the Cocoa sources I’ve seen)…”, I couldn’t understand it. I’ve always viewed Unix and Cocoa source with my standard setting setting of 4 and it looks OK. I’ve never used emacs (or indeed any Terminal-oriented editor since the WordStar days), so that sort of problem seems to be restricted to people who don’t like Xcode…

    I always use tabs and it seems 99% of the code I see also does. As my own code often indents a lot and I still code (sometimes) on smaller screens, I’ve always set my tab width to 4. Very rarely I see code that looks weirdly indented on my IDE, and it’s always because of spaces.

  5. Michael Dippery Says:

    What about in the case of function arguments that wrap, such as this:

    int x = MyFunction(int a,
                       int b,
                       int c)

    (I don’t know if that will turn out to be formatted okay, but you probably get the gist — the “ints” in the parameter list should all be lined up).

    In this case, it seems spaces should be used, otherwise the ints won’t line up; i.e., if I set my tab width to 8 and line them up with tabs (and an extra space or two or three, if necessary), it won’t look right to you if your tab width is only 4.

    So is that to say that it’s best to use tabs except in cases of aligning parameter lists (or, in the case of Objective-C, aligning parameter names according to colons)?

  6. Peter Hosey Says:

    Michael Dippery: Yes, it applies to C too, despite the absence of colons: Xcode should use tabs until it matches the indentation of the start of the statement, and then continue with spaces.

  7. Rolf Howarth Says:

    The solution to the problem of misaligned code that contains tabs and spaces is not to eliminate tabs, nor is to try to standardise on how wide a tab should be rendered, but to NEVER, EVER, EVER under ANY CIRCUMSTANCES use SPACES for indentation.

    Using tabs preserves the semantic structure (ie. indent level) whereas spaces are just a lazy way of getting something that maybe looks roughly right at the time. It’s exactly the same as when people screw up MS Word or HTML documents by including low level character formatting rather than defining styles. I hope no one is going to argue that because a mixture of styles and low level formatting is bad the answer is to put in abominations like <b><font size=+1> everywhere instead of writing <h1> !

  8. David Smith Says:

    Is there anywhere we can file a bug report on emacs about this odd behavior it has? I’d love to see the myth of spaces-for-indenting-makes-sense die a well deserved death.

  9. Alan Says:

    Thanks for the interesting post. As you correctly point out, using tabs for “indentation” necessitates the use of spaces for “alignment.” Here’s the problem: People invariably screw this up. Yes, as simple as it sounds to you and me, there are tons of programmers out there who will never get it. They can’t wrap their heads around the difference between indentation and alignment, no matter how many times you explain it. Their code is a haphazard mess of tabs and spaces, and they don’t even know they’re doing it.

    Now, let’s say you work on a project with 500 other programmers. Do you have time to track down each offender and give them an earful for using tabs and spaces inappropriately? Even if you did, it wouldn’t matter—they’ll just do it again. While it may be technically superior to argue “tabs-for-indentation-and-spaces-for-alignment,” it doesn’t scale in practice.

    The only way to stop the insanity is to tell everyone to use all spaces all the time. This is simpler to grasp conceptually. Even the dumbest programmer can understand what “all-spaces” means, sort of. They’ll probably still leave trailing whitespace all over and maybe the occasional tab, but the result is still much cleaner than an inconsistent mix of tabs and spaces.

  10. Mike Says:

    Tabs are great except for when they need to define a set number of characters. Michael Dippery pointed out one example of this. The other is if you line up values in large variable blocks or define statements.

    Things like this:

    #define this 8
    #define longerthis 5

    If someone has a longer tab stop when they format the code, the values may not line up for users of smaller tab stops.

  11. Peter Hosey Says:

    Mike: Absolutely—we should all use spaces to align columns. That’s different from indenting a line, which should be done with a tab.

  12. Joel Says:

    I’m pretty sure the only way we’re ever going to solve this is to wait until emacs acquires time traveling capabilities, then go back in time and stop emacs from being created.

  13. mind Says:

    The problem with tabs arises when you want to create columns on lines that aren’t indented the same amount. Under your rules..

    if foo:         # some inline, but
            bar()   # multi-line comment

    (with a tab to indent the second line) will only look correct if tab stops are set to 8. if you use tabs to make the column for the comments, then that will fail for various tab sizes and line lengths.

    so, now you’ve got the worst of both worlds. tab stops must be set to the size that the file was edited with in order to look correct, and you’ve got to set that size. simply putting spaces in makes it so that people can’t adjust the indentation to suit them, but that there is no ambiguity with further columns.

    further, if someone does edit your file and uses spaces for indentation, then your columns again break when the tab stop is changed. you’re better off adopting a concrete layout in the file, and any people who edit it need to follow a certain style. tabs don’t solve all of the problems, therefore they just add complexity.

  14. mind Says:

    well uh, apparently my comment didn’t format the same way the preview did, even though i put it in a pre block. but think about standard python indentation with inline comments that line up

  15. Peter Hosey Says:

    mind:

    if you use tabs to make the column for the comments, then that will fail for various tab sizes and line lengths.

    That’s why you shouldn’t use tabs to make columns. I said this in the post: Use spaces for columns. Only use tabs for indents.

  16. zachrahan Says:

    Peter, I agree with you entirely, but mind has a valid point. With your “tabs for indenting scopes, spaces for alignment” rule, there is no way to ensure that comments on lines with different tab-indents align properly. Go back and look at mind’s example, and format it with tabs for the indents, and spaces to make the comments line up. Then change the tab width, and the comments no longer line up.

    I think this is a minor issue compared to the utility of tabs otherwise, but it is an bona fide issue.

  17. James Cunningham Says:

    > I said this in the post: Use spaces for columns. Only use tabs for indents.

    Isn’t that something of a pain in the rear, though? I use the tab key to insert a large amount of horizontal space. Sometimes I’m indenting; sometimes I’m lining up columns. Not only would I dislike having to consider what sort of horizontal space to insert this time, I don’t want to have to hit the space bar umpteen times for correct alignment—not when my text editor is quite good about managing this with the tab key.

    I’ll keep using spaces. It’s simpler for me, never seems to cause trouble, and is presently the accepted thing to do. (Besides, the thought of someone viewing my code with an eight-space indent? It’s enough to make me cringe!)

  18. Peter Hosey Says:

    zachrahan: Wow, you’re right. Thanks for pointing that out.

    That comment syntax is pretty, and I would love a programming language or an editor with inherent support for that. I can’t say I can recommend it with modern tools, however, mostly because of the issue mind and you raise.

  19. Peter Hosey Says:

    Not only would I dislike having to consider what sort of horizontal space to insert this time, I don’t want to have to hit the space bar umpteen times for correct alignment—not when my text editor is quite good about managing this with the tab key.

    Copy-and-paste. ☺

    For indentation, all good editors offer auto-indentation. One keypress and it’s indented correctly.

    I’ll keep using spaces. It’s simpler for me, never seems to cause trouble, and is presently the accepted thing to do. (Besides, the thought of someone viewing my code with an eight-space indent? It’s enough to make me cringe!)

    Why do you care?

  20. petoi Says:

    I wish all editors supported emacs style File-Variables. This neatly solves it on a file by file basis.

    /* -*- tab-width:4 -*- */

    put that in your first line of your file (or second for script files) and it will automatically set the tab width. Most programmers have the width set to something when they create a file. All this has to do is match that setting. If you find a file with the wrong width, you can add that line at the top of the file and voila problem solved, and you don’t have to go through and change the entire formatting of the file with a tabify or untabify command, which will end up causing problems for scm systems. Lets get this in Xcode, TextMate and VisualStudio (if it does not already support it*).

  21. Matt Di Pasquale Says:

    @mind, @zachrahan, and @Peter Hosey, we can solve the inline comment issue by putting a tab indent after the first, non-indented line, like so (where \t denotes a tab):

    First, let’s line it up without any tab indents:

    if foo: # some inline, but
    bar() # multi-line comment

    Now, let’s add the tab indent:

    if foo:\t # some inline, but
    \tbar() # multi-line comment

    Since, the tab indents will be the same length, this should always line up.

    I don’t know of any editors that support this kind of behavior, however, but it shouldn’t be so bad to do manually since this kind of commenting is probably rare, right?

    P.S. I wish Xcode supported this kind of tab for indent & spaces for aligning columns. Cool!

  22. Peter Hosey Says:

    Matt Di Pasquale: That doesn’t scale to multiple levels of nesting, and honestly, that style of comment is way too fragile no matter what you do. Any major rearrangement and/or reindentation of the statements will screw it up, and changing the indentation means removing tabs you won’t notice are there at first.

    I may allow a comment on the same line as a statement if both are very brief, but then there is nothing about the comment to line up across multiple lines. Most of the time, comments should go on lines all to themselves.

  23. Mecki Says:

    DON\’T USE SPACES FOR INDENTION! ALWAYS USE TAB FOR INDENTION!

    If a tab expands to 2, 4, 6, 8 or whatever spaces places absolutely no role! It will always look right to the viewer of a file, since the viewer just sets tabs to whatever size *HE PREFERS* (every tool, even tiny CLI editors like nano/pico allow you to do this!) and it will always look nice and correct to him.

    When using spaces, you FORCE YOUR PERSONAL PREFERENCE ONTO THE REST OF THE WORLD! Just because you may like 4 spaces for indention, the rest of the world may not agree with you, I know plenty of people that have other preferences. And if ever other people must work on your code and they have a different preference, they will just plain hate you every day they must look at your code.

    How tabs map to spaces is only relevant if you use tabs for LAYOUTING. I did not say use it for LAYOUTING, I said use it for INDENTION, that\’s by far not the same. Somebody asked what about something like this:


    int x = MyFunction(int a,
    int b,
    int c)

    Despite the fact, that I consider this an extremely ugly way to break long lines, you would write it like this:


    ::::int x = MyFunction(int a,
    ::::...................int b,
    ::::...................int c)
    ::::printf(\"x is %d\\n\", x);

    \”:\” means INDENTION and is done by TAB characters (one or more, depending on your current indention level) and \”.\” means SPACE and is PURE LAYOUTING.

    Try this in an editor of your choice with tab set to 4 spaces. Now set tab to 2 spaces. Does it look right? YES, IT DOES! Set tab to 8 spaces. Does it look right? YES, IT DOES! You see, it plays no role how many spaces are used to express one tab character, the only thing that the tab size changes is the distance between the beginning of the text and the left screen border. The tab size only make the indention more or less obvious. Some people prefer small indention (only two spaces per tab), others cannot read that, they want 8 spaces to make the indention really obvious (also consider people of different age, with different viewing capabilities, etc.). And if you code like that, both can and both will be happy, both get their favorite intention. It does not break your layouting to intend with tabs, it will only break your layouting if you failed to understand what the difference between indention and layouting is.

  24. Peter Hosey Says:

    Mecki: So, basically what I said in the sequel to this post. http://boredzo.org/blog/archives/2008-11-05/tabs-vs-spaces-redux

Leave a Reply

Do not delete the second sentence.