98 comments

[ 3.2 ms ] story [ 158 ms ] thread
> if you need more than 3 levels of indentation, you’re screwed anyway, and should fix your program.

I hope some JS programmers read this

functions and closures to the rescue
The first professor who I took a programming class from told us that anytime we needed to indent something we should be asking ourselves if we needed to create a new function and most times the answer would be yes.

It was good advice.

If your function is only called once it's better to leave it indented than to create a function.
Depends on the context/language I think. Splitting a block of code into a function gives it a greppable name that (ideally) succinctly describes the point of the block. You can have that block just headed by a comment inline to try and do the same thing, but comments are extremely prone to getting ignored and left to grow outdated when the purpose of the code around them changes. It's easier for the reader to just mentally abstract away that block if it's separated into a function. And if that block is worth testing, it's much easier to do when it's isolated into its own function.

There's reasons to keep the block inline too--like if you're working in a more resource constrained environment, or if the block isn't really isolated/self-contained enough to cleanly abstract away, or if the block in question isn't really doing anything substantial. But I wouldn't say that you should only abstract away code into functions to save yourself from literally copy/pasting the lines in multiple places.

You could argue that it's better to make the function; the compiler will probably inline it if it's only used once, and it helps avoid having the logic duplicated if it's needed again (or by lazy copy/paste programmers).
My point is not performance but that a function used only once is likely just contributing to one conceptual block of code. But now it's artificially divided up and one has to jump around to understand that code as a whole. There are exceptions of course but blindly adding functions to avoid indenting is probably going to lead to a harder to follow solution.
in js you can just leave the named function inline. i sometimes create functions that call themself so that i can just move it out if the code needs to be repeated, and i dont have to clutter the scope with more variables. with es6 js got bracket scoping, but i still prefer function scope and closures.
I referenced this quote when my team was bikeshedding over our linting configuration, and I was pushing for a lower line limit.
I think it is a bad idea to apply that rule to all programming languages though. If you are doing Java, for example, and have a function inside of a class, and a loop in the function, putting a condition inside of the loop would violate the rule, and that is a pretty common thing to do.
Consider it from a base level rather than indent 0, and it will work for Java.

The kernel guideline doesn't mention this due to being C specific. C++ can exhibit similar issues.

Early returns ftw (if (!cond) return; instead of if(cond) do();)!
Why are anonymous functions so popular in JS? Why not just name the callback?
Because Java is a kingdom of nouns, and for the most part (method handles, which are just fancy reflection objects, aside), you can't name a function without creating a closure.

Anonymous callback:

  Foo.foo(() -> bar());
Named callback:

  private final class MyFooDelegate implements FooDelegate {
    @Override
    public void doFoo() {
      bar();
    }
  }

  ...

  Foo.foo(new MyFooDelegate())
You forgotten about method handles.
(comment deleted)
Why name it if the name will never be used?
The name shows up in the stack trace, and you'll have a much better idea which path the code hit when it crashed.
naming things are hard and some programmers are willing to sacrifice their left hand if it means they can write shorter syntax.
several indentations in js is most of the time linear async flow, while in other languages it usually means branching complexity. while an experienced js programmer can deal with async code and avoid the so called callback hell, nesting the callback doesnt make it any harder to understand, just harder to test, debug and refactor.
I think these guidelines are something every C programmer should atleast have read and reflected about.

I've been coding according to these guidelines for quite a while with some minor modifications:

Always use curly braces (especially when you're not alone coding in the codebase as it allows for easy modification without code ending up out of the intented scope).

80 char width; well I don't follow this one as I really do prefer long lines over broken lines for readability.

Other than that I think these guidelines are great and offer consistency and easy to read code, in my opinion.

80 char width helps encourage you to "do less stuff in each line", rather than "break lines at 80 chars".

But I agree, after a few levels of indentation (which is sometimes unavoidable) you end up struggling.

Also, when a function takes five arguments, and each_argument_looks_like_this, it's easy to hit 80 chars!
Depending on the verbosity of the name, even a no arg function might exceed 80+ chars. We have a few instances of test convenience functions in our non-C code base, though they're exceedingly rare.
Maybe pass them in a struct?
As long as you give up all hope that you'll be passing values in registers.
Most of the time, it doesn't matter. And if you have lots of arguments, you're going to spill anyway.
If the call doesn't cross compilation units, you still might. Or possibly with LTO, but I've less experience there.

That said, it's certainly a concern worth being aware of where it's relevant.

The linux kernel USB API has a few functions that take a bunch of arguments. For example:

int usb_control_msg(struct usb_device * dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void * data, __u16 size, int timeout);

So it is kind of hard to avoid going over the limit.

What we end up doing is break each argument into one line, like this (I hope the formatting comes out all right):

int res = usb_control_msg(

    dev,

    usb_sndctrlpipe(dev, 0),

    0x41,

    ...
);

It's not so much of an eye-sore.

Ahhhh I see. Nortel had that coding guideline actually. Just in their case, the first argument followed the parenthesis. The second argument (and the rest) followed the first argument column.
That's a good approach, as can be the struct. An option not mentioned yet is to give things smaller local names.
Those are good reasons. The reason I accept an 80 char limit, even if I prefer longer lines, is because it affects everyone else. Short lines will always fit, in every editor, in every terminal, over ssh, etc.. Long lines, anything over 80, will compromise some workflow somewhere. Long lines will generally mess with someone's flow, and they become more inconvenient for more people the longer the lines get.
> over ssh

Line length isn't going to have much impact on what fits over ssh...

True, thanks. Did you not know what I meant? "in every terminal, over ssh". Line length can affect my use of grep as much as it can affect remote vi sessions. I'm not talking about the protocol, I'm talking about the existence of multiple workflows that are all affected by line length conventions. The edit/compile cycle in your favorite IDE isn't the only consideration, and I'm not the only person to consider when I decide how long my lines should be.
SSH is only relevant in that it might provide an additional motivation for operating in the terminal, in some cases.

That said, yes, I was picking a nit mostly because the nit itself amused me (and I hoped it would amuse others) - it wasn't meant as significant criticism of your post.

You can say that enforcing an 80 character line is outdated, but I often find myself looking at code reviews in GitHub on my smartphone, and it dramatically improves readability in that environment.
I haven't said it's outdated though just that I don't always follow it. Desktop Github version seems to show 125 chars (including -+) and on my iphone I'm below 50 char width by default.

I just prioritize to have "readable" code in my editor as first prio, second would be sites like Github on desktop and if it works good on a mobile phone that's a plus but not high prio for me.

(To be honest though I do actually glance through some Github reviews on my phone, but since it's pretty limited I do find myself thinking that I should get to a computer and do a real review there instead.)

I turn my phone to landscape first.

And, yes, if any meaningful comments are needed I wait until I'm back at my laptop.

Are there any languages where code-style is required to be a certain way? or it won't compile. I mean full authoritarian about everything, not just based around white-space like python.
Some have little that can differ, such as assembly, but not that I know of at least. Some define a "proper" way, though (PEP-8, gofmt, and doesn't rust also have a rustfmt?).
rustfmt exists, and has come a long way recently, but is not quite ready to be distributed with Rust by default yet. Soon though!
The gnat Ada compiler has style warnings, which, together with -Werror, ensure that you don't write non-conforming code.
Yes, old fixed-format languages like FORTRAN and RPG (the older versions). Everything had to be written in specific columns, due to their punched card heritage.
Fixed-format FORTRAN isn't actually that rigid. The first six columns have special semantics, but apart from that, the compiler is not going to enforce a certain style.
Go uses gofmt, which pretty much achieves what you're looking for. Lisp code, emacs generally will generally just clean up idiomatically though that's less what you're looking for.
This is where Go really dropped the ball. They had the perfect opportunity to move lint and fmt (and vet and the rest) into the compiler. I don't for the life of me understand why they did not.
Many teams configure their CI systems to fail builds if they don't pass style checks. This can be great if there's some situational flexibility since no autoformatter is smarter than a human that knows the code, but terrible if there's a formatter (like prettier) that defines only one true format and sometimes gets things horribly wrong.
> A C programmer would call that variable tmp, which is much easier to write, and not the least more difficult to understand.

Yuck. Abbreviations are usually bad. For one thing, people tend to pick different ways to abbreviate things so an hour later when you're trying to grep for where the variable was used you have to try to remember whether the variable was written tmp, temp, or temporary.

In this example, "temporary" is probably the least useful thing to know about the variable, since most variables are temporary. How about "counter"?

Short idioms are essential for concision in specific domains though.

My favorite example: I was writing some graphics code recently, and some reviewer said: "I don't understand this uv abbreviation that appears all over the code. Please expand it."

headdesk.gif

(For those who aren't familiar: u and v are conventional names of texture coordinates, like x, y, and z are conventional names of spatial coordinates. There's no abbreviation to expand.)

I am sure you are right, but I can't figure out what "xy" would mean in a program, either. Perhaps all the points on a plane normal to z?
I used to use x and y as my generic loop iteration variables until I worked on code that involved two-dimensional coordinates, and suddenly x and y had very specific meaning.

I skipped to i and j, but I am certain there are mathematicians out there who find that confusing, too.

I guess, it's about context, too, at least a little.

> I skipped to i and j, but I am certain there are mathematicians out there who find that confusing, too.

IME it's pretty standard in most of mathematics to use "i" and "j" as index variables when e.g. doing sums and products over indices/sets. (Though I suppose you could be talking about the use of i, j, k as axes?)

I think in Quaternions, i, j, and k are the non-real components.
Good point, I'd missed that one! :)

What does one for index variables in that notation? Or is the main point of q notation that you don't need index variables? ;)

In some sense this is true, but also, I strongly prefer Arc<T> to AtomiclyReferenceCounted<T>.

It's all about knowing what should be abbreviated and what shouldn't.

I think abbreviations make sense if the abbreviation is better known than the full name, or if it's standard jargon in the problem domain, or it's used so frequently in the program that it can be considered a convention. But using abbreviations just to save some typing makes life difficult for newcomers to a project and can be a source of bugs.
Not really. Go also encourages short, concise names, and it seems to work pretty well... or at least not be causing significant headaches so far as I can tell.

Why would one be forgetting and grepping for tmp? That's the kind of variable that would be inside a function (so max one or two screenfuls, according to the guidelines here), and more realistically would usually be declared and used entirely within the same block of a dozen or so lines of code. Nobody is going to forget whether it is temp or temp while reading those 12 lines of code. And using temporary is just silly -- I can't think of a single time I've ever seen that used in any language.

Maybe grepping for the variable is a bad example. I have seen bugs where a variable has been accidentally defined twice though due to inconsistent abbreviations.

I'm struggling to think of any case where "tmp" couldn't be replaced by a better name. It's always going to be a temporary something.

I'm not arguing against short, concise names where they won't cause problems. Variable names of e.g. x, y, i all have their place.

You should not need to grep for the local temporary variable, whether it's named tmp or temp or anything else -- because it should be right there in front of you since your functions should not be more than about a screen page in length.

If you have a variable tmp that's defined 6 pages back or worse, is global -- consider refactoring first, not just a more descriptive name.

I tend to follow these guidelines with the following exceptions:

1. always use curly braces

2. 4 spaces instead of a tab character

I violate #1 in only one case, the entire statement has to be one line:

    if (var1 != var2) return false;
But having it on two lines and tabbed out without braces seems extremely ugly and dangerous to me.
100% agree with both points. Though I'm the crazy guy who does

    #define unless (x) if (!(x))
so I can do

    unless (f = popen("ps -axf")) die("popen of ps -axf");
I use a tab character with vim tabstop and shiftwidth set to 4.
> Inconsistently using braces for function declarations.

> Not using braces for conditions followed by single line statements.

> Using abbreviations.

This is enough for me to say fuck this guide.

> Encoding the type of a function into the name (so-called Hungarian notation) is brain damaged - the compiler knows the types anyway and can check those, and it only confuses the programmer. No wonder MicroSoft makes buggy programs.

This refers to systems hungarian, the brain-dead twin of apps hungarian. The latter can be useful in C.

The article has sentences like:

> First off, I’d suggest printing out a copy of the GNU coding standards...

Which suggests this was written from the point of view of a specific person. In which case, it should attribute the author somewhere. I couldn't find a byline anywhere. Also, no date.

You should run `git blame` on the file in the Linux repo.
Did you try it? I think you'd find it's part of the initial git commit on 2005-04-16: Linux-2.6.12-rc2

You could look in one of the historical trees.

I think it's just trying to emulate a colloquial tone, actually being written by a number of individuals (or at least being updated by multiple people).
I can not find proof right now, but I've read some version of this in the early 2000s and the (initial) author is Linus Torvalds. You should be able to tell by the style :)
"It’s a mistake to use typedef for structures and pointers."

C programmers tend to forget this wise recommendation.

I have always though it was good practise (because it reduces the verbosity) ; Could you please prove me wrong? :-)
The only thing I can think of is that you could have

    int mytype;
conflicting with

    typedef struct { ... } mytype;
By putting the "struct" in front you only potentially conflict with other struct definitions.

Is that the thinking or is it something else?

Yes, for pointer types for example type qualifiers (e.g., const or volatile) cannot penetrate typedef of pointer types. So if you use typedef with pointers you are stuck.
It varies a bit by context; Linus's call is likely the right one in the context of the kernel, though to be sure it's not so big a deal that other approaches can't be made to work fine.

You call out one upside - decreased verbosity. The accompanying downside is usually - as in this case, for both structs and pointers - that potentially relevant information is made less visible. Whether it's a good idea depends on how likely that information is to be actually relevant, weighed against the upsides.

Where performance is relevant, you want to be aware of where you're potentially passing large structs around. Putting this info in the type name instead is an option (eg _s), and can reduce verbosity a bit. I don't know of any good reasons to call out structs that are sized like primitives.

The case against hiding pointers is much stronger. Level of indirection can be relevant to performance, and is often relevant to correctness, particularly where mutation is involved. You can put it in the type name, but it's not going to be any less verbose - a star is already one character. I allow a possible exception here for where an opaque type is allocated by a library and passed around as a handle and the client really doesn't need to ever know whether it's a pointer or an int index or what.

This coding style works, sure. But so does the NT kernel style, which is the opposite of Linux kernel style in many ways. For example, in NT, all structs and pointers are typedefs. So what? There's a certain elegance to type names being one token long.

People bikeshed source code formatting. They make arguments about developer portability and visual minutiae, usually without any sort of evidence, when in reality the higher-level organization of the code matters a lot more. Optimizing code for readability quickly runs into diminishing returns and begins hurting a lot more than it helps.

In general, I'm strongly against rules and ceremony and centralization when it comes to coding. Ostensibly, it's all about hygiene and best practices and whatever, but in practice this kind of discipline amounts to "I like it this way and you're going to like it too". It's far more important to let developers make decentralized, rapid decisions about the best approach for a particular task than to enforce some kind of useless Procrustean uniformity on everyone.

The two styles work because they are not being mixed with their opposite. A cached shed optimization is what makes working with others bearable.
That’s sort of what the parent is saying: “the only code formatting rule that matters is consistency, everything else is bollocks”. But people want to think there’s some kind of higher justification for their preferred style, so they come with these just-so stories that don’t have any real evidence behind them.
Also, it's local consistency is what matters, not global consistency. Developers can adjust to slightly different coding styles when they move to a new project. They're going to have to learn new abstractions, new bits of domain knowledge, and new coworker-interaction approaches anyway. Style is a very minor concern.
The word consistent is an issue in and of itself. You probably mean you think it's best when a style is applied consistently. But probably don't care that the style itself values consistency. The style linked above is not remotely consistent. Braces in different columns. Braces styles for functions different from non functions. if statement can have or into have braces. All extremely inconsistent
ITYM "regular", not "consistent".
I always "preach" the value is in uniformity ! Weather you camelCase or kebab-style or using my-super-foo-style as long as everyone on the team uses the SAME style. Thats a WIN ! THATS where the value is !! Not in the RIGHT style but in the SAME style.
Amen to that! I have my preferences on where I place my curly braces, and I have strong opinions on camelCasing versus using_underscores. But for a given code base, consistency outweighs any advantages any given coding style might have.

Also, emacs has glasses-mode, so when I have to work on a code base that uses camel case, at least I don't have to squint really hard. ;-)

The biggest take-away from this, in my opinion, is that it's better to just type `struct foobar` instead of typedefing `foobar_t`. Structs should be treated differently than scalars, and therefore you should write them differently. The #1 principle of any coding style should be to optimize for readability and clarity over writability and convenience.

Also, please limit your lines to 80 characters. https://sr.ht/toyc.jpg

> 80 characters

Assuming indentation is counted against those 80, why not use a bigger monitor?

Did you see my link?

More practically, a lot of people (myself including) will split their monitor vertically to consider lots of code at once, and talk about code in emails via patches on mailing lists and such where 80 columns is the norm.

It would be nice to have this integrated into clang-format.
It doesn't have to be integrated. It shouldn't be hard to produce a .clang-format configuration file for this coding style.