Yesterday, I shared some spicy takes. A few were particularly controversial—most notably, that I correct Gif the correct way (with a soft G)—but I also got a lot of emails asking me to elaborate on a few of them.
Today, I wanted to talk about how tabs are objectively better than spaces. This won’t take long.
Tabs let you define how big you want each indent to be, and spaces do not.
I’ve always wondered why some people tout “forcing a consistent appearance across environments” as a pro for spaces. That’s a bad thing.
To be honest I’m surprised code format converters aren’t ubiquitous. Let the repo have it’s master format, enforced on commit. Then converters translate into each developer’s preferred standard dialect on checkout and back again on commit.
The consistent appearance thing is probably more about how mixing tabs (for indentation) and spaces (for alignment, eg in multi-line function definitions of calls) looks like complete crap if you change the tab width.
I think you have it backwards. If you use tabs for indentation and spaces for alignment it works great for any tab size.
It is when you use a tab just as a compressed representation of 8 spaces and use them for alignment as well that it goes to shit. (because you have made the sin of tab == 8 spaces instead of the correct tab = 1 indent level)
How does that work, and with which editor settings? If you simply set the tab width (tabstop) in vim, things go south.
Say you have a function definition one indent level in, then 22 characters of text. You more want to align the next line to that. How does that work in practice with tabs?
The obvious way with tabs and ts=4 would be 6 tabs and two spaces(one tab for the initial indent, the rest to match 22 characters). But then someone with ts=2 comes along and barely gets half way there, or someone with ts=8 who overshoots by a lot.
That’s not how you should mix tabs and spaces for alignment. You use the same number of tabs as the previous line, and then fill the remaining width with spaces. That way, when you change tab width, the alignment spaces will always start in the same column as the line they’re aligning to, regardless of the tab width.
Do most editors do that by default? If so, that’s great – if not, it’s just a downside for tabs, if you need to hit enter, backspace out the automatic indents and then press space 30 times rather than just hit enter and have it aligned automatically.
vim seems to auto-insert tabs when you hit enter mid-function definition, at least with standard settings.
That seems like a problem with Vim, then… Typically I don’t align at all, so I’m not familiar with editor behavior for alignment; I prefer to just indent one level deeper.
Setting tabstop and shiftwidth differently is basically legacy braindead behaviour. It is going back to the logic of tab is just a way to compress spaces. If you are doing that then you have all of the problems of both tabs and spaces.
As for alignment the easy answer is don’t use tabs for alignment. Use tabs for indentation, if you want to align something use spaces for the alignment past the indentation. Lemmy seems to be breaking code snippets right now but I have a really old blog post about this.
The post is a little out of date when referencing the style of my blog but the C example shows alignment. If you can resize the browser you can see that the indention changes from 4 to 2 as the screen gets narrower without breaking the alignment.
Using only tabs for indentation and only spaces for alignment will never result in crap alignment when adjusting tabstops because the alignment does not use tabs.
This is using both tabs and spaces for alignment.
--->func foo(int i,
--->---> int j);
Observe what adjusting the tabs does,
->func foo(int i,
->-> int j);
This uses only spaces for alignment,
--->funcfoo(int i,
---> int j);
When converted the alignment is maintained because the tabstops aren’t used for alignment, only for indentation.
I’ve always wondered why some people tout “forcing a consistent appearance across environments” as a pro for spaces. That’s a bad thing.
To be honest I’m surprised code format converters aren’t ubiquitous. Let the repo have it’s master format, enforced on commit. Then converters translate into each developer’s preferred standard dialect on checkout and back again on commit.
The consistent appearance thing is probably more about how mixing tabs (for indentation) and spaces (for alignment, eg in multi-line function definitions of calls) looks like complete crap if you change the tab width.
I think you have it backwards. If you use tabs for indentation and spaces for alignment it works great for any tab size.
It is when you use a tab just as a compressed representation of 8 spaces and use them for alignment as well that it goes to shit. (because you have made the sin of tab == 8 spaces instead of the correct tab = 1 indent level)
How does that work, and with which editor settings? If you simply set the tab width (tabstop) in vim, things go south.
Say you have a function definition one indent level in, then 22 characters of text. You more want to align the next line to that. How does that work in practice with tabs?
The obvious way with tabs and ts=4 would be 6 tabs and two spaces(one tab for the initial indent, the rest to match 22 characters). But then someone with ts=2 comes along and barely gets half way there, or someone with ts=8 who overshoots by a lot.
That’s not how you should mix tabs and spaces for alignment. You use the same number of tabs as the previous line, and then fill the remaining width with spaces. That way, when you change tab width, the alignment spaces will always start in the same column as the line they’re aligning to, regardless of the tab width.
Do most editors do that by default? If so, that’s great – if not, it’s just a downside for tabs, if you need to hit enter, backspace out the automatic indents and then press space 30 times rather than just hit enter and have it aligned automatically.
vim seems to auto-insert tabs when you hit enter mid-function definition, at least with standard settings.
That seems like a problem with Vim, then… Typically I don’t align at all, so I’m not familiar with editor behavior for alignment; I prefer to just indent one level deeper.
Setting
tabstop
andshiftwidth
differently is basically legacy braindead behaviour. It is going back to the logic of tab is just a way to compress spaces. If you are doing that then you have all of the problems of both tabs and spaces.As for alignment the easy answer is don’t use tabs for alignment. Use tabs for indentation, if you want to align something use spaces for the alignment past the indentation. Lemmy seems to be breaking code snippets right now but I have a really old blog post about this.
https://kevincox.ca/2014/06/26/responsive-tabs/
The post is a little out of date when referencing the style of my blog but the C example shows alignment. If you can resize the browser you can see that the indention changes from 4 to 2 as the screen gets narrower without breaking the alignment.
Using only tabs for indentation and only spaces for alignment will never result in crap alignment when adjusting tabstops because the alignment does not use tabs.
This is using both tabs and spaces for alignment.
--->func foo(int i, --->---> int j);
Observe what adjusting the tabs does,
->func foo(int i, ->-> int j);
This uses only spaces for alignment,
--->func foo(int i, ---> int j);
When converted the alignment is maintained because the tabstops aren’t used for alignment, only for indentation.
->func foo(int i, -> int j);