Well, there are some situations where tabs don’t work. For example:
struct kobject {
const char * k_name;
char name[KOBJ_NAME_LEN];
struct kref kref;
struct list_head entry;
struct kobject * parent;
struct kset * kset;
struct kobj_type * ktype;
struct dentry * dentry;
};
If you try to use tabs to align the right column, if someone is using a different tab width from you, it will turn out all wrong. The solution, as bitwise has mentioned, is to either not align things like that, or to use spaces in this special situation.
This is an interesting one. I’ll summarize it for those who don’t know much about programming.
To understand this debate, you need to understand how a program that’s written by a human is turned into a program that the computer can understand and run. The human produces source code written in a programming language. The source code is input to a special program called a compiler. The compiler takes source code and translates into machine code, which is what the machine can understand. Now hold this thought for a minute, because now we need to understand what “types” are.
All data has a type associated with it. For example, there’s the int type, which indicates that the data is an integer. There’s the string type, which is a sequence of characters like “abdc”. Programmer can also define their own types. The code I gave above is an example of a programmer defined type, in fact.
Now, different types are usually incompatible. A string is not an integer. It might be possible to interpret a string as an integer – for example, “100” could be interpreted as the number 100 – but in most languages, you have to explicitly convert between strings and integers, or any other incompatible types.
If different types are incompatible, it’s an error to try and treat them as the the same type. For example, 100 - “foo” is meaningless – you can’t subtract “foo” from 100. The difference between static and dynamic typing is in where this error is detected. In a statically typed language, the type of everything is known to the compiler, so it can find these types of errors and report them. In a dynamically typed language, the type of a variable is not known to the compiler. We only know what type a variable contains when the program is running, so that’s when the error is detected.
For example, take the expression x - y. If x and y contain integers, then this expression is meaningful. It’s an error is y contains a string, though. In a statically typed language, the source code will contain enough information to allow the compiler to decide whether the expression is valid or not. In a dynamically typed language, we must wait until the program is run to see if this was an error.
My opinion: dynamically typed languages are a gigantic step backwards. It’s an established fact that errors detected by the compiler are orders of magnitude cheaper to fix than errors detected at run-time. Even worse, in a statically typed language, all of your type errors will be found by running the compiler once, because the compiler can recover from errors and proceed. When a type error is found a run-time, however, the program cannot proceed; it must crash. This means that for every one of your type errors, you must run your program once to find it. This is much more time consuming.
The article bitwise linked to makes the argument that programs can contains bugs even if there are no type errors, so you have to write tests for your program anyway. That’s true, but for a statically typed language, some of those tests are done for you. I can’t see how that’s a bad thing.
I used to hate K&R style as well, but I was forced to use it* at my last 2 jobs and I’ll admit it’s grown on me. The ease with which I switched styles has convinced me that any debate over the merits of the different styles is pointless.
- Actually, I was forced to use the Java style, which is almost the same but not exactly.