Ah, C. The best lingua franca language we have… because we have no other lingua francalanguages.
C is fairly old ― 44 years, now! ― and comes from a time when there were possibly more architectures than programming languages. It works well for what it is, and what it is is a relatively simple layer of indirection atopassembly.
Alas, the popularity of C has led to a number of programming languages’ taking significant cues from its design, and parts of its design are… slightly questionable. I’ve gone through some common features that probably should’ve stayed in C and my justification for saying so. The features are listed in rough order from (I hope) least to most controversial. The idea is that C fans will give up when I complain about argument order and not even get to the part where I rag on braces. Wait, crap, I gave itaway.
I’ve listed some languages that do or don’t take the same approach as C. Plenty of the listed languages have no relation to C, and some even predate it ― this is meant as a cross-reference of the landscape (and perhaps a list of prior art), not a genealogy. The language selections are arbitrary and based on what I could cobble together from documentation, experiments, Wikipedia, and attempts to make sense of Rosetta Code . I don’t know everything about all of them, so I might be missing some interesting quirks. Things are especially complicated for very old languages like COBOL or Fortran, which by now have numerous different versions and variants and de facto standardextensions.
“ Bash” generally means zsh and ksh and other derivatives as well, and when referring to expressions, means the $(( ... )) syntax; “Unix shells” means Bourne and thus almost certainly everything else as well. I didn’t look too closely into, say, fish. Unqualified “python” means both 2 and 3; likewise, unqualified “Perl” means both 5 and 6. Also some of the puns are perhaps a little too obtuse, but the first group listed is alwaysC-like.
#include is not a great basis for a module system. It’s not even a module system. You can’t ever quite tell what symbols came from which files, or indeed whether particular files are necessary at all. And in languages with C-like header files, most headers include other headers include more headers, so who knows how any particular declaration is actually ending up in your code? Oh, and there’s the whole include guardsthing.
It’s a little tricky to pick on individual languages here, because ultimately even the greatest module system in the world boils down to “execute this other file, and maybe do some other stuff”. I think the true differentiating feature is whether including/importing/whatevering a file creates a new namespace . If a file gets dumped into the caller’s namespace, that looks an awful lot like textual inclusion; if a file gets its own namespace, that’s a good sign of something more structured happening behind thescenes.
This tends to go hand-in-hand with how much the language relies on a global namespace. One surprising exception is Lua, which can compartmentalize require d files quite well, but dumps everything into a single global namespace bydefault.
Quick test: if you create a new namespace and import another file within that namespace, do its contents end up in thatnamespace?
Included: ACS , awk, COBOL , Erlang, Forth, Fortran, most older Lisps, Perl 5 (despite that required files must return true), php , Ruby, Unixshells.
Excluded:Ada, Clojure, D, Haskell, Julia, Lua (the file’s return value is returned from require ), Nim, Node (similar to Lua), Perl 6, Python,Rust.
Special mention: ALGOL appears to have been designed with the assumption that you could include other code by adding its punch cards to your stack. C#, Java, OCaml, and Swift all have some concept of “all possible code that will be in this program”, sort of like C with inferred headers, so imports are largely unnecessary; Java’s import really just does aliasing. Inform 7 has no namespacing, but does have a first-class concept of external libraries, but doesn’t have a way to split a single project up between multiplefiles.
Optional blockdelimitersOld and busted and responsible for gotofail :
if (condition) thing;
New hotness, which reduces the amount of punctuation overall and eliminates this easy kind oferror:
if condition { thing; }
To be fair, and unlike most of these complaints, the original idea was a sort of clever consistency: the actual syntax was merely if (expr) stmt , and also , a single statement could always be replaced by a block of statements. Unfortunately, the cuteness doesn’t make up for the ease with which errors sneak in. If you’re stuck with a language like this, I advise you always use braces, possibly excepting the most trivial cases like immediately returning if some argument is NULL . Definitely do not do this nonsense, which I saw in actual code not 24 hoursago.
for (x = ...) for (y = ...) { ... } // more code for (x = ...) for (y = ...) buffer[y][x] = ...The only real argument for omitting the braces is that the braces take up a lot of vertical space, but that’s mostly a problem if you put each { on its own line, and you could just not dothat.
Some languages use keywords instead of braces, and in such cases it’s vanishingly rare to make the keywordsoptional.
Blockheads: ACS , awk, C#, D, Erlang (kinda?), Java,javascript.
New kids on the block:Go, Perl 6, Rust,Swift.
Had their braces removed:Ada, ALGOL , BASIC , COBOL , CoffeeScript, Forth, Fortran (but still requires parens), Haskell, Lua,Ruby.
Special mention:Inform 7 has several ways to delimit blocks, none of them vulnerable to this problem. Perl 5 requires both the parentheses and the braces… but it lets you leave off the semicolon on the last statement. Python just uses indentation to delimit blocks in the first place, so you can’t have a block look wrong. Lisps exist on a higher plane of existence where the very question makes nosense.
Bitwise operatorprecedenceFor ease of transition from B , in C, the bitwise operators & | ^ have lower precedence than the comparison operators == and friends. That means they happen later . For binary math operators, this is nonsense .
1 + 2 == 3 // (1 + 2) == 3 1 * 2 == 3 // (1 * 2) == 3 1 | 2 == 3 // 1 | (2 == 3)
Many other languages have copied C’s entire set of operators and their precedence, including this. Because a new language is easier to learn if its rules are familiar, you see. Which is why we still, today, have extremely popular languages maintaining compatibilit