142 comments

[ 1.8 ms ] story [ 325 ms ] thread
This makes a whol lot of good points. C is really not suited for most modern applications, and some of its design choices are questionable (to say the least).

On the other hand: In my opinion, C is a good language to show beginning programmers how memory management is done. Basically, it could be a third learned language, after a scrip language (like python) and an OOP language (Java or C++).

It's hard to figure out which are the good points and which are hyperbole. "EDSAC was the second electronic digital stored-program computer to go into regular service" and with "the world's first assembler" dating from 1949, says the Wikipedia reference. As such, many languages can be traced back to that "Most Godawful Computer on Earth (TM)".

Or, "4. ALGOL60 [4] had many nice features of languages today but was too theoretical: only existed on paper." Yet its reference 4, at https://en.wikipedia.org/wiki/ALGOL_60#ALGOL_60_implementati... , lists a number of ALGOL60 implementations even in the 1960s.

Algol60 could never be taken seriously as a mainstream language, lacking Algol-like syntax.
Algol60 lacked Algol-like syntax?

In one sense, that's almost tautologically impossible. I presume you mean "like the other Algols", which makes it a non-absurd statement, but... could you expand or clarify what you mean here?

I believe it's a joke. Gold isn't a gold-like substance because it is gold. Algol isn't an Algol-like language because it's Algol.

If so, I don't believe it's a good joke.

It's a joke, but not the one you thought.

The joke is that the syntax of Algol didn't actually look all that much like what people mean when they say "Algol-like syntax". No curly braces anywhere to be seen.

As my introduction to Algol-like languages was through Pascal, the joke completely passed me by.
I could see that putting you in a place where encountering people misusing "Algol-like" to mean C-like, when trying to distinguish from something more like Algol, would be extra noticeable and annoying.

Likely you have just been lucky :)

I hang around computational chemists. They wouldn't say "Algol-like" in the first place. So, double lucky!
Hyperbole and reference will probably be worked on in next one. ALGOL mostly existed on paper or in complex compilers on expensive systems. The "computer" that is the EDSAC is not on that list. So, they would have to build their own ALGOL... a difficult thing anyway in that period... using a machine and process like this:

https://youtu.be/6v4Juzn10gM?t=3m41s

Can you imagine trying to make a robust, fast compiler for such a weak machine in tape batches on that weak machine? I could understand them giving up with BCPL as a useful result on that machine. The cost-benefit analysis of using a PDP-11 like an EDSAC is opposite. Wirth et al showed you could do better. C inventors just liked BCPL. I'd understand if they used BCPL to bootstrap a Modula-2 or other ALGOL subset with good design. Instead, they kept trying to force BCPL to do something it wasn't designed for... wait, it wasn't designed period lol. It was just what compiled on a 1940's calculating device.

It's bad foundations vs others' good foundations. Might have worked for them sure. Today, though, we need to know why they did it that way and why we definitely shouldn't if we don't absolutely have to. That's the point of the essay. Plus, to clear up associated misinformation.

(comment deleted)
It's often said that you won't be a programmer until you understand pointers, but my own experience was the other way around: I didn't understand pointers until after I had been programming in languages like C# and Java for a long time.

I had first started out in C, hacked around for a while, even made a few simple programs. But I wasn't ever able to get very far with it. My first Java app had me doing GUI programming right away and I was making things a lot faster than I could have ever done in C.

The problem was, there are two things a beginner needs to learn: how to solve problems with programming, and the particular programming language they are using. So I think it's best to keep one of those things very, very simple. And you tend to lose a lot of people from boredom if that thing is the types of problems you're solving. So we have to default to the language being simple.

Pointers and memory management are an implementation detail. They are important, but they aren't the job. The job is fulfilling requirements. Some days the requirements might be "fit inside this tiny box of memory", but it's far more likely to be "so, like, when I click on the link, the screen flickers. <you mean a new page loads?> Yeah, whatever, just make it stop flickering."

Calling UNIX Bad by Design... oh my gosh!
Much of it is, because they had to squeeze big concepts like those from Multics into very small minicomputers. I don't know how much memory they had on their PDP-7, but for the original PDP-11 it would have been 64K each for data and instructions, with some of that almost certainly taken for the memory mapped hardware (one of the 11's innovations). The PDP-11/45 allowed a user program the same, with 8K taken from normal data space for the stack.
If you thought that was entertaining, you'll get a kick out of reading the old UNIX Hater's Handbook.

http://www.vbcf.ac.at/fileadmin/user_upload/BioComp/training...

In particular, at least check out Page 36, showing an articulately hand-drawn Dennis Ritchie, in stoic defiance, blowing raspberries at UNIX critics through his thick-rimmed glasses. It's by far the high point of the book.

The nice part about the Unix Haters book is that the authors were actually familiar with their subject material :-).
The funny thing about gcr's comment is that I posted ESR's review of UNIX Hater's Handbook here before. He agreed with a good chunk of it while noting other stuff changed or was ranting. As for your comment, I came to C after using alternative, system languages that compiled faster, crashed less, and ran fast. After I figured out basics of C & read secure coding books, I said "F#$@ this!" It was an unnecessary and horrible development experience. So, I coded up a 4GL that let me develop in industrial BASIC that compiled to C apps with checks in to use their compilers and libraries. Macro'd out boilerplate. People thought my "C code" was fast and thorough but I never wrote in that garbage language except prototyping the tool. (Long gone so don't ask.) My application areas were OS-level stuff, games, AI, networked apps, and GUI apps. Shows how easy it would've been to ditch given I sucked at compiler design.

Eventually, I discovered how it was created, that most proponents claims weren't accurate depiction, and did this write-up. Glad you enjoyed that I kept it close to subject material from its inventors. Meanwhile, the C inventors went onto design, with Pike's help, a better language that solves most of C's problems while maintaining a Wirth-like level of safety and development. Pick up Go language if you want to know what that experience is like. Personally, I'm more for a subset of Modula-3 with Rust's dynamic safety, SPARK Ada's extensions for correctness, and macro's for DSL, productivity benefits. Or efficient functional one with similar properties.

Unix may be fine by 1960s/1970s standards, but leaves a lot to be desired now, or probably since the 1980s. Unix has bad approaches to things like file locking (see VMS instead), the shells are a hot mess, and C causes a lot of problems. Even the Unix developers themselves went on to work on Plan 9.
Good overview. Wrong conclusions.
> There's a recurring theme where people think the C language's design is good for systems programming, even today. These people think that someone sat down, thought of every tradeoff, and made the best ones for system programming.

Or, people think that a good practical language can only come from using it to solve real problems and iterating on the design. Sitting down and conjuring up the perfect language just does not work.

Now, I am the first to agree that C has plenty of flaws. For one, the integer types are just a mess. I will be happy if something can replace it, but so far most attempts have ended up either too slow or too complicated.

What is your opinion of Rust? Does it fall under the category of "too complicated"? Or is it simply far too young to be proven yet?
I'm curious about Rust but haven't tried it enough yet to form an opinion.
Intend to spend this years Christmas holidays catching up on Rust.
Rust is extremely interesting, but here's the thing: at this point it's going to be hard to dethrone a language like C++. Far too many excellent libraries exist for C++ and even recent but slightly more mature languages like Go will need at least a decade to catch up. Sure, you could write bindings but it's never going to be quite the same.
This is probably true. It also applies to Java, C#, PHP, COBOL, x86, and ARM. Although, it might be noted that various things on that list dethroned others in certain segments. Still doesn't matter. You don't have to dethrone anything.

Just build an advantageous ecosystem with a good chance of short-term and long-term commercial support. Do that right, there will be uptake that can slowly build into something competitive. We've seen it happen numerous times with Mathworks (MATLAB), Borland (Delphi/Pascal), Eiffel (Eiffel), Microsoft (C#), Sun (Java), Google (Go), Mozilla (Rust), and Apple (Swift). Really useful and superior tech can also attract people into closer-knit, small, and high-quality communities as we see with Haskell. So, I tell people not to worry if vast majority will use your stuff. Just worry if a good chunk of them will find it useful enough to pick up and extend. Then you might make it.

Note: A cheat I've already mentioned is making your language or whatever integrate with a popular one and its libraries. Clojure, in an anti-LISP world, is proof of how unusually effective that can be. The typical example is how most C alternatives can compile to C or use its libraries.

If you look at big C++ apps like Firefox or AAA games, most of the third party libraries are used through C apis. Even commercial libraries written in C++ often have C based APIs because of historical binary compatibility problems in C++ linking. Is safely wrapping a C api in C++ significantly easier than wrapping it in Rust?
An example of convergent evolution -

Apple was primarily a Pascal shop back in the early 80s, when they were developing the Lisa. But since bog-standard Pascal is useless for systems programming, Apple extended it with things they needed: Pointer arithmetic (not auto-scaling by base type, so you were constantly multiplying by sizeof), oh yeah, sizeof, and a bunch of other stuff I've forgotten. Pascal's miserable I/O system was left on the floor. NEW was largely replaced by a heap manager that worked for small systems. And so on.

So what Apple wound up with was "C, with nested procedures". The two languages were nearly identical in semantics. I could spend a day hacking on something, and if you asked me in the evening which language I'd been using I would have had to think about it.

The types of bugs that people wrote didn't seem to vary depending on the language they chose.

My informal survey is that other companies extended their versions of Pascal, making similar choices. These companies (Apollo? a few others) have sunk without a trace.

Languages that look a lot like C are what you get from systems level people doing near-bare-metal programming. You're not going to fix the language, you need to convince the people doing the work that you have something better. And for the most part, something better doesn't exist.

You can write low-level systems in higher-level languages; Microsoft's Midori was an OS from the metal-up in C#, with a lot of interesting deep thinking about abstractions and security. SmallTalk / SELF / FORTH are interesting until you need to scale. LISP Machines didn't take the world by storm in the 80s, and we didn't get very interesting fallout from all the LISP-based OS work that was being done. ADA . . . I know one guy who loves ADA, but he can't tell me what he works on.

Successful languages seem to be the result of commercially successful systems, not the other way around.

This is exactly what I think of when I read this kind of unilateral negativity about C. I love writing programs in C. I don't need to learn any new semantics ever. Even the integer types, as parent was complaining about, have been largely cleaned up(I use (u)int(32,64)_t and (s)size_t and everything just works.

The problem space is well understood, the tools are widely-deployed and reliable, the performance is excellent. I just don't see how people can be so negative about something which has been so immensely useful without getting in the way.

Op probably meant that C integer types are a mess because they underflow, overflow or result in undefined behaviour at the smallest mistake. :)
Languages that look a lot like C are what you get from systems level people doing near-bare-metal programming.

On CISC computers back in the day, K&R C statements supposedly resulted in an average of 2.7 machine language commands.

You're not going to fix the language, you need to convince the people doing the work that you have something better. And for the most part, something better doesn't exist.

You mean, something better enough that it justifies the short term cost of moving to a different language/environment.

"Or, people think that a good practical language can only come from using it to solve real problems and iterating on the design. Sitting down and conjuring up the perfect language just does not work."

I agree. It's why that's a false dilemma I'm not promoting. I illustrated a C alternative that iterated out a language and OS better than C/UNIX in a number of metrics. We'd provably have fewer problems like crashes or hacks had we been stuck with that language. Static analysis would've been easier as well given its successor had the first, standard library that was mathematically verified to avoid specific problems.

https://news.ycombinator.com/item?id=12316092

The conclusions do not follow from the facts presented
This completely misses the key factor: (most) computers were not big or fast enough to run high level languages back then. So for people who only got to use small computers, they had to code in assembler (or cross-compile on a big computer).

So anyone who could come up with a high(er) level language that would run on smaller machines was a winner. It meant the difference between a life of misery hacking assembler or a life of milk and honey coding in a high level language. There was no "Oh, let's be stupid and omit <safety feature du jour> from our language". It was more like "Wow, I got a compiler to run on that PDP-11/20 and it only had to be split into 5 passes to fit into core".

There were many many simple languages back then that occupied this niche. Only C has survived into the modern era (thanks to open source compilers and Unix and Microsoft's adoption of C for WindowsNT). If things had turned out differently we would be complaining about the lack of safety in code stil being written in Bliss or IMP or PL/S.

Huh? The conclusion was "the worst aspects of UNIX and C were intention design decisions that had nothing to do with what a good, systems language should look like and everything to do with limitations of an EDSAC, PDP-7, & PDP-11."
> This completely misses the key factor: (most) computers were not big or fast enough to run high level languages back then.

The first paragraph leads with that key factor: "Even back then, there were better languages/OS's that cheap hardware just hadn't caught up to. That cheap hardware's limitations, and them alone, drove the design decisions of BCPL, B, and then C."

This completely misses the key factor: (most) computers were not big or fast enough to run high level languages back then

From the 1st paragraph, that's what I thought they were going to talk about:

totally contradicts real reasons why C is designed the way it is. Even back then, there were better languages/OS's [1] that cheap hardware just hadn't caught up to. That cheap hardware's limitations, and them alone, drove the design decisions of BCPL, B, and then C.

But you're right, they're mostly talking about the compilers.

So anyone who could come up with a high(er) level language that would run on smaller machines was a winner.

I think that's what the Objective-C folks were thinking. I guess the author would lament that they chose C as the other partner.

Only C has survived into the modern era

Forth was around in those days. So was Smalltalk and Lisp. So maybe, "survived as more than a niche language?" (Forth and Smalltalk are really from the 70's. You could argue that only Lisp is from "those days.")

Fortran and Cobol are still widely used.
"Only Lisp" from my short list above. There are others.
I suppose I was thinking of the set of languages commonly used back then for programming smaller computers. Smalltalk definitely wasn't aimed at small machines. I guess I just woudln't include Forth and Lisp in that set. Not sure exactly why to be honest. Forth certainly ran on very small machines. Both were considered "weird" where I came from, but I acknowledge that isn't a universally held view ;)
Lisp first ran on a vacuum tube computer, it was definitely "small", and it can certainly run fairly nicely on current "small" machines, although not "very small" like I recall of Forth.

Smalltalk's machines weren't all that large by today's standards, memory was 128kB, expandable to 512kB per Wikipedia, but expansions were addressable only by bank switching. 128kB is the same memory budget as the Unix PDP-11/45's for a single process.

Didn't know Smalltalk constraints were similar. Might have to add Smalltalk in the essay somehow. Which computer did LISP start on again?
Thanks! Wow. That's some tough hardware to work with. So, both Fortran and LISP developed on the same machine. Guess I just never thought about the LISP hardware given I started with the AI books & went straight to modern work. Had I done so, I'd have wondered less about whether it could work in constrained environments haha.
You view things perhaps too much through modern eyes.

For them, that was some amazing hardware to work with. A lot of new ideas were tested out on the 704.

Oh yeah, Im sure. Im using modern eyes intentionally in context that many people think a LISP or dynamic language can't run on constrained hardware. The 704 was pretty constrained by today's standards. Refutes that claim.
No, you weren't using "modern eyes intentionally in context". You wrote "I'd have wondered less about whether it could work in constrained environments", and your description comes across as unintentional. That's what I'm referring to.

I grew up with microcomputers. My first machine had 16K, which I think is about what a 704 had. There's a lot you can do with 16K. Altair BASIC ran on 4K machines. Even modern micropython, meant for microcontollers, works in 64K, and that language wasn't designed to be space efficient.

Oh, and from http://edn.embarcadero.com/article/20693 :

> Turbo Pascal version 1.0 shipped on one floppy disk. The total number of files on the disk was 10. Total floppy disk space used was 131,297 bytes. Total size of TURBO.COM (including the integrated development environment with compiler, Wordstar-style editor, and run-in-memory system) was 33,280 bytes.

"No, you weren't using "modern eyes intentionally in context". You wrote "I'd have wondered less about whether it could work in constrained environments", and your description comes across as unintentional. That's what I'm referring to."

You're putting a lot of weight into words I wrote casually. The intended meaning was that I hadn't investigated what a good, useful-today implementation of LISP could do in what would be a highly-constrained system today. Particularly, on cheap microcontrollers used in embedded systems. I have seen LISP's like femtolisp that use around 1K for implementation with good performance. So, original LISP being able to run on a 704 implied quite a lot to me.

On other hand, BASIC and Pascal are low-level, static 3GL's. They're easy to compile efficiently into efficient code even without much optimization. I pulled it off for BASIC without knowing much about compilers. I'm sure it took talent to get the mix of functionality and efficiency in stuff like TurboPascal or some of the Altair apps. It's not telling me much about how a dynamic LISP will work on those platforms, though, given its execution style is so different. Especially if I code in a way that doesn't penalize readability or maintenance for efficiency.

Fortunately, there's a pile of papers I can go through if I want to determine an optimal way to do that. Many of which are from 70's-80's. The 704 result just tells me embedded impact could happen if I kept it efficient. Micropython work is also impressive. I hope the point of my reply is more clear now.

I've seen emulations of those early Smalltalk machines running at simulated speeds. It was definitely "leisurely." A word to the wise tech innovator: It doesn't matter how amazing the technology is, wait until you can deliver good UX.
There was no "Oh, let's be stupid and omit <safety feature du jour> from our language.

Exactly. I think once you drop down into assembler it becomes very apparent what C's goal was - to standardize assembly language and make cross CPU coding easier.

If you code in assembly, you eventually need to begin writing your own routines for copying, appending strings/memory, etc. It begins to look suspiciously like the stdlib as it evolves. And consequently, your code begins to look like C with assembly mixed in for the control logic.

The big difference is your source only works on your target CPU. Drat.

"Exactly. I think once you drop down into assembler it becomes very apparent what C's goal was - to standardize assembly language and make cross CPU coding easier."

That's not in the docs I cited from the inventors. The original goal was ALGOL's benefits. Too hard on an EDSAC. So, chop off anything until you have fastest, most minimal language for an EDSAC. Thompson preferred BCPL over anything else at the time & wanted to do an OS in it on a PDP-7/11. Ritchie too. Mods needed to do that became the earliest C.

Portability across CPU's is never mentioned. UNIX and C were specifically designed for those hardware with holdovers in there to this day. A portable, assembly language from back then that was successful was P-code. It hid CPU/architecture differences, stayed efficient, and was backend of the compiler. Later, they wrote OS's in a mix of HLL's and P-code variant. Exactly the kind of thing you do if designing for portability.

C was ported because it barely had any features. These ports often had to be kludgingly forced into the machines. Quite different.

"If you code in assembly, you eventually need to begin writing your own routines for copying, appending strings/memory, etc. It begins to look suspiciously like the stdlib as it evolves. And consequently, your code begins to look like C with assembly mixed in for the control logic."

You might evolve a stdlib. What it looks like, how safe it is, how efficient it is, and whether it's auto-customized per CPU via macros, etc are totally different depending on your foundation. Most of Oberon System's OS is done like you would ordinary programs with function calls, type-checked arguments, and even garbage collection in later ones. The LISP machines were incredibly different with one able to use macros for API's or bottom layers. The Smalltalk approach was more high-level than most 3GL's with its OOP philosophy. House hid what little unsafe stuff it had in the H-Layer with rest of OS coded in Haskell with type-system & functional benefits.

So, C's style is neither inevitable nor the best. It was a byproduct of its inventors development style on two, specific machines starting with a typeless, low-level language designed for a worse machine. On other extreme, today there's people coding OS kernels, drivers, etc in theorem provers that automatically extract code in C or assembly with safety and/or termination proofs. The only C compiler, CompCert, that passed fuzz testing bug-free in middle-end used such a method whereas those written in C had tons of bugs despite years of bug-hunting. Drat.

The LISP machines were incredibly different with one able to use macros for API's or bottom layers.

Or drop down to microcode, were things like eval and the garbage collector tended to live, along with graphics primitives and the usual things at that level. The first widely produced Lisp Machine could be outfitted with a very large bank of this vertical microcode if you were willing to pay for the static RAM to populate it. There was also a Lisp to microcode compiler of some level of utility.

I found a paper on a compiler from Pascal subset to microcode. All kinds of cool stuff one can do if microcoded & open for extension. LISP microcode is new to me. You have a link on that one?
No, all word of mouth, often from the microcoders themselves (Moon and Greenblatt, at least). And it was either a compiler from Lisp to microcode, for making hot code faster, or Lisp primitives implemented in fast microcode. And the virtual machine that implemented the compiler's byte code as well.

  It hid CPU/architecture differences
That's not a binary thing. If you hide too many differences you're internalizing limitations of the worst contemporary architecture.

Also, I think it would be odd if someone, who as you carefully summarized had written assembler for a half dozen different architectures, didn't have in mind some loose notion of portability. And, FWIW, I remember reading at some point that Ritchie and/or Thompson felt some sort of vindication when they discovered how easy it was to write a C compiler for other machines.

"C as a portable assembler" is something of a straw man. I don't think anybody with even a passing familiarity of the actual history believes that Ritchie and Thompson had such specific and directed goals.

Another way to spin the history is to argue that you had two engineers who had programmed on a wide variety of architectures, and who were familiar with a wide variety of languages, create a new language that was simple to compile and made pragmatic assumptions about architectural dependencies. For sure they weren't trying to create a universal assembler. Rather, they were driven by pragmatic needs.

Allowing yourself to be directed by actual, manifest requirements without foreclosing future opportunities is arguably one of the best approaches to engineering.

Did C succeed despite it's conceptual shortcomings, or _because_ of them? I think your essay presumes the former without giving enough consideration to the latter.

That's actually a good counterpoint that I'll have to think on further. We get a feel as engineers for what might work. They could in fact have internalized heuristics for building portable software that factored into their decisions without them consciously deciding to. I don't know if that's what you're saying but it's what I thought when I read it. Very interesting.
" If things had turned out differently we would be complaining about the lack of safety in code stil being written in Bliss or IMP or PL/S. "

Quite selective. The obvious counterpoint, which next version of essay will utilize, would be Wirth and Jurg's work in Lilith system. Two pro's + 8 part-timers, two years, plenty constraints, and whole system from HW to OS to apps to do with Pascal philosophy (balancing safety/speed/maintenance). To keep apples to apples, let's focus on language design the main two went with: the Modula-2 language. It's very simple, compiles faster than C due to cleaner grammar, is safe-by-default in a few ways, lets you turn that off where necessary, modular, supported concurrency, and runs fast. This approach got repeatedly iterated into progressively safer and faster systems by amateurs & pro's alike, sometimes just 1-2 students, that crashed less than C apps. Best part relevant to your comment: they built first Modula-2 compiler on a PDP-11. :P

Most of C's advantages, a few more on top of it, fewer problems, and developed on same architecture. Sounds like a clear winner from design standpoint. If you want simpler & faster, the Edison system is a good, extreme example of how ALGOL practitioner might do a C-like project. Too simple vs Modula-2 imho but beats C at its own game of fast minimalism with some Wirth-style benefits. Whole system, with source, got published in one paper.

http://modula-2.info/m2info/pmwiki.php/General/FAQ

https://en.wikipedia.org/wiki/Modula-2

http://brinch-hansen.net/papers/1981b.pdf

OK, Lilith took less time to write, and it was (also) on crummy hardware. Was it as useful as Unix was in production? Lilith never really took off. Either it wasn't as capable as alternatives, or it was marketed badly. My money is on "not as actually useful as theory predicted", though I have no evidence.

I note that Borland brought out a Turbo Pascal to go with Turbo C (fixing some of Pascal's shortcomings in the process), but never bothered with a Turbo Modula-2. If Modula-2 was better than Pascal, why not? (And, since the initial article seemed to be a fan of Algol, why didn't Borland do a Turbo Algol?) Microsoft had their Microsoft C that competed with Borland, but didn't bother to do a Microsoft Pascal. Why not? Because nobody cared about Modula-2, and not enough people cared about Pascal. And why was that? Because in practice, it was easier to write software in C than in Modula-2, or even than in Pascal.

You're idealizing dead languages. They're dead for a reason, and it's not just historical accident. In the real world, they were harder to use than the languages that theory said should be the best.

"My money is on "not as actually useful as theory predicted", though I have no evidence."

The point was a research prototype they could use with safety and consistency down to CPU with hopefully benefits for teaching students. It succeeded at all that. The Ceres workstations that followed ran ETH university for a while. Quite useful. The students easily learned, modified, ported, etc the Oberon OS's. The hyperlink-driven GUI, mocked in that time, is also similar to the default method of obtaining information today that you're using to read this.

"but never bothered with a Turbo Modula-2. If Modula-2 was better than Pascal, why not?"

What does a company's marketing strategy tell us about the technical merits of a programming language? I don't know. It could be anything up to and including there were more C and Pascal users so they built C and Pascal compilers. Was my guess.

"And, since the initial article seemed to be a fan of Algol, why didn't Borland do a Turbo Algol?"

I'm fixing that part in next revision. It's coming across to some readers, including you, that I'm pushing for ALGOL itself to be built by 2 people on a PDP-11. That's... probably impossible. It's instead showing C did not have to be built, others did better with ALGOL subsets, most dominant (and productive) languages today have more capabilities in common with ALGOL than BCPL, and therefore that's the approach programmers should aim for instead of C's non-design. The constraints behind BCPL stopped being justified before PDP-11 existed. They definitely aren't now given all the alternatives.

" Because in practice, it was easier to write software in C than in Modula-2, or even than in Pascal."

This could be true for older versions. The question would be whether getting an app working... from prototyping to compiling to debugging to integration... is easier in C than in Wirth languages. I seriously doubt that just reading C programmers' own writings and bug reports. The minimal, extra amount of work is some typing you do that takes minutes but can save hours. They parse & generate faster but type-check more. So, compiler speed probably depends on implementation.

Hard for me to evaluate this one given students all over learn a language like Oberon easily while they struggle with C. Pros struggle to make C correct. How can C be less work?

"They're dead for a reason, and it's not just historical accident."

If that's true, then COBOL is still alive and well processing much of the world's critical transactions on its most reliable systems for a reason that's not historical accident and has everything to do with superiority of COBOL's design. Possibly why non-UNIX OS's dominate desktops, too. If popularity implies better design or usability, then perhaps people that believe that should buy Microfocus COBOL for Windows for their next app to test the theory. Whereas, an ALGOL subset & variant like Modula's, Oberon's or recently Go is probably way easier to work with due to design decisions per my theory. More effective in other metrics outside raw performance, too. Take your pick.

Note: You should also consider psychological and financial factors in the uptake of IT solutions. It might surprise you to find out that many popular things weren't necessarily the most effective solution. Or even most cost-effective.

The best things always have detractors, yet they live on.

C is over 40 years old and is still in widespread use today, even though there have been (supposedly) better languages targeting the same use cases for decades now. There is more to it than just inertia.

I've been using it less and less over the years, mostly because I've also been tackling less and less problems in the domains where it excels. It is still my favorite language though.

Definitely agree there. I've moved through several languages - c, c++, java and now python. Over the years my work has progressed from real hard development to more of an analysis & design & architecture role. So, when I decided to get back in touch with programming and complete problems on online judges as a hobby - I just went with c/c++ (c++ just to use the template library).
I respectfully disagree. I think C is a giant disaster that will continue to plague us entirely due to inertia. You're really underestimating how entrenched it is. Try to go program on micro in something other than C. Try programming a driver. The toolchains simply don't exist - the comparability with several decades of work is also shaky.

C is a terrible cross platform assembly b/c it doesn't allow you the level of control which one would expect in this day and age. Maybe it was written in the days when CPUs were brain-dead simple, but basic things that are available on most architectures aren't part of the language. It has no notion of a CPU cache, it has no notion of branching (you can't tag a likely branch and an unlikely one), it even goes as far as to ignore user keywords for inlining and provides no keyword for blocking inlining. RVO is implicit magic that you just pray happens. Const != immutability.

When you write C you have no notion of what the compiler is going to output whatsoever

A lot of these things are available through compiler extensions (so great, now it's not cross-platform), but even still the language is broken. "Expert C Programming" has a really long chapter that goes over all the more confusing subtly problems C has beyond just arrays being pointers-but-not-really.

I have to use C pretty regularly and all I can think is "God.. why hasn't anyone made this better yet"

> When you write C you have no notion of what the compiler is going to output whatsoever

Well, sort of... but what language isn't that true of? And really, what language isn't that more true of than C?

Higher level ones who's compilers can better reason about the code? See for example more generally Fran Allen's comments in Coders At Work for how desolate a optimization wasteland is C.
I think you're looking at this backwards from clifanatic's point. The more a higher-level compiler can reason about the code, the more optimizations it can perform, the less the compiler output resembles the source code input I gave it.
It has no notion of a CPU cache, it has no notion of branching (you can't tag a likely branch and an unlikely one), it even goes as far as to ignore user keywords for inlining and provides no keyword for blocking inlining. RVO is implicit magic that you just pray happens. Const != immutability.

What language has all of these covered? (Rust?)

> branching

None that I know of from the last 20 years or so.

> inlining

This one is tricky. Sure, GCC ignoring the inline keyword is annoying, but sometimes figuring out if something is inline-able is undecidable, and sometimes things just aren't capable of being inlined.

> blocking inlining

This is something that I don't understand why we don't have. Sure, lots of times you would rather inline than not, but sometimes inlining the wrong expression can subtly change the performance / lookup / dispatch semantics in weird ways. I've no hard examples of this, but it should be easy enough for almost every language to have a --no-inline flag.

> RVO

C++11 and up somewhat handle this with move semantics. Having a separation of rvalues / lvalues certainly helps, but the distinction could probably be clearer, as it can get to be a gruesome and hairy topic depending on your types.

> const != immutability

That you can often just "cast the const away" is really the problem here. I think Rust improves on this space as there is no safe cast to remove const-ness, but if you ever find yourself or somebody else casting away const, you / they should realize that they are going against the design of the system. I'm still undecided if "mutable" keyword in C++ classes is so horrible in this context, as it certainly helps in making const-types usable with the rest of the language (which is inherently non-const).

Well, in C++, "const" isn't actually intended to mean "literally const", it's supposed to mean "const in spirit". You can be const and still change some of the bits of an object if that change doesn't (semantically) change the object. To be able to do that, you need to be able to cast away const-ness. (No, I can't think of a reasonable example at the moment.)

It would be fair to ask whether that intent was a good idea. It would also be fair to point out that many (most) times people cast away const-ness simply out of expediency, rather than using it the way it was intended.

'C' is quite sufficient for micros and drivers. Micros are supposed to be small. Python's catching on with the kids for systems programming because of Arduino. Drivers are ... well, drivers. If you write 'C', you have to look at the assembly.

And adding inline assembler to manage the hardware - caching, that sort of thing - is just how it's done. Maybe it's possible to make a library ( or a bunch of #defines ) that manages this for you?

I really am sorry you have to use it, but there are rather large populations of people who go completely untroubled with it.

"And adding inline assembler to manage the hardware - caching, that sort of thing - is just how it's done. Maybe it's possible to make a library ( or a bunch of #defines ) that manages this for you?"

The current ones might be like C on that part, might do something different. I haven't evaluated that. Here's a few:

http://www.astrobe.com/Oberon.htm

http://www.mikroe.com/mikropascal/#

http://www.mikroe.com/mikrobasic/

Why is it we're not all using Oberon again? :) Ah, I remember having meetings about things like that... now that was inertia.

SFAIK, ( meaning I don't really ) the various "Mikro" probably do the same thing that the native toolsets for , say PIC, do and simply add default, built-in symbols for bits in control registers and things like turning interrupts off/on. That keeps the amount of assembly further down.

I feel confident in saying that because they'd want to make defection from the default say, AVR compiler chain as painless as possible.

And of course, Linux drivers/ioctl() help a lot with bringing kernel mode things into user space. Using assembler in a driver is just one of those things we find acceptance with.

right, but once you start writing inline assembly it's no longer a "cross platform assembly" - and I think there is a demand for that. For instance the Linux kernel supports a mind-boggling amount of different architectures and "just write in-line assembly" probably doesn't cut it

"large populations of people who go completely untroubled with it."

Stockholm syndrome.... Or a lack of imagination about how things could be better. There really is a cult-like adoration of C - I think mostly by people that don't use it and learning it in college and long for something as simple

You forgot old people. I'm positively ancient :)

I've used it for 30+ years. It's not the only thing I've used. But I've used it a lot, for deployed systems that Just Work(ed)(tm). They don't have a lot of UI, usually.

You don't get "cross-platform assembly" in a complete 100% sense and get the enjoy the fruits of hardware diversity. An escape to inline assembly is PERFECTLY FINE.

The Linux kernel doesn't use that much assembly. But yeah - "just write inline assembly" is exactly how it's done. And as someone who's done a few Board Support Packages for Linux and other O/S-es, it's not a huge risk - there's generally a port for a processor, so a small number of people write the inline assembly and it gets distributed widely.

I don't think people who don't like 'C' should use 'C'. I don't like Java too much, so I pretty much refuse to work in it. I don't touch Web stuff at all, beyond a few weeks here and there.

You have to use a pretty weird process to write robust and reliable systems in 'C' but it's not exactly difficult. It just takes... six months of knuckle busting most people don't have. Maybe three months. You have to smooth over learning some arcane rules.

I think that the emphasis we see online on 'C' comes from open source, in particular the Linux kernel, and it causes this weird ...status rift to open and people fall into it.

I'd been programming professionally for twelve or so years prior to even seeing Linux. In 'C', Pascal for a couple years before 'C'. I FIRMLY believe that 'C' is the mechanism to get the most reliable and deterministic solution to problems. Note I didn't say "fastest". But that's when I do it; after the last few years, there's enough whining about how bad 'C' is that I kinds shrug and say "hey, if it hurts you're doing it wrong. Make yourself happy."

but I'm on teams with people who use all the fancy high level stuff and ... frankly, it's not my stuff that doesn't work. Because I've done this for 30+ years.

"C is over 40 years old and is still in widespread use today, even though there have been (supposedly) better languages targeting the same use cases for decades now. There is more to it than just inertia."

A false argument that's easy to counter with itself:

COBOL is over 50 years old and still in widespread use by businesses, even though there have (supposedly) been better languages targeting the same use cases for decades now. There is more to it than just inertia.

Nope, it's just inertia. Specifically, the risk of breaking stuff by rewriting it to work better, preservation of backward compatibility as a business benefit, prior investments in tooling, what people are already using vs what's new, and laziness far as hobbyists that started with it. Same reasons C and UNIX problems didn't get fixed. Sociology and economics trump technical points in adoption or continuation of languages, platforms, etc. The meme for this, per Gabriel, is "Worse is Better."

First: No, that's not the point of "worse is better". The point is that usability comes from rapidly iterating as you run into real problems, not from an ivory-tower design. The first paragraph of Gabriel's essay says: "The concept known as 'worse is better' holds that in software making (and perhaps in other arenas as well) it is better to start with a minimal creation and grow it as needed. Christopher Alexander might call this "piecemeal growth." This is the story of the evolution of that concept." This has nothing to do with inertia and old code bases preserving old things.

Second: I would suspect that there are far more new projects started today in C than there are in COBOL. C is in use (at least sometimes) as a voluntary choice, and COBOL is not.

"First: No, that's not the point of "worse is better". "

I hear lots of interpretations of it. I could be misremembering. I'll take another look at it. Appreciate the specific points to look into. :)

"Second: I would suspect that there are far more new projects started today in C than there are in COBOL. C is in use (at least sometimes) as a voluntary choice, and COBOL is not."

If we're using that constraint, then Java and PHP are the example with Java for about any kind of app. A lot of people even like Java. They think it's great and hate C. So on and so forth. Yet, the popularity still tells me nothing as I got to watch Java get forced by Sun and others into its position step-by-step with insane amounts of work to get it to match performance and productivity of better-designed languages... partly successful there. It was actually experience of Java IIRC (later C#) that was the reason many people didn't think an OS could be done in a GC'd language. They mentally associated GC'd language with stuff like that. That a small team pulled it off in Oberon with a handful of people at a time porting or extending it is always news to Java developers I meet here.

Lack of context, bad design, meant to replace prior languages for most applications, platform orientation, and massive ecosystem + uptake + tooling make it a better comparison. So, I'll use it and COBOL next time.

Off topic: Do Enterprise Java and COBOL feel kind of the same to you? I mean, as languages they're completely different, and yet...

Maybe anything that winds up trying to play in that "language for big corporate enterprise software" winds up like that?

Nah, the experiences are extremely different. Old-school GUI's with files locked up in mainframes vs Netbeans with tons of middleware, ANT, etc. Java has all these frameworks, model-driven development with UML, and so on. COBOL is probably strict waterfall with Java shops taking on forms of agile. Huge differences even if I see some superficial similarities.

What they have in common, as with C, is they were pulled or pushed in for ecosystem benefits + marketing then spread and sustained on that bases mostly. So much stuff written in them with so many talent or tools that they're self-perpetuating. I'm claiming most C adoption & entrenchment is the same phenomenon outside embedded or ultra-high-performance applications where use is quite practical. You constrained the COBOL example but I recalled popularity effect applied to Java, too.

You are missing my point by focusing on just half of it. I like C and know a few more people that like it too. I'd happily start new projects in C as long as they fit C's (traditional) use cases.

I don't think anyone really likes COBOL. And I don't think anyone would start a new project in COBOL given the choice.

Your main point is you like it. The other is it's popular, which is non-technical. Glad you enjoyed using it, though. I'm all for programmers enjoying their work. Everyone's preferences are different on that.

"I don't think anyone really likes COBOL."

COBOL was like the Visual Basic of mainframes. It looked close enough to pseudo-code or business problems that even lay people could learn to write mediocre code in it. They could Get Stuff Done. Versus assembly, ALGOL, or C, they'd prefer it since they can grok it. Truth is that those people probably don't need to be coding business-critical apps but that plus mainframes making compilers for it gave COBOL an adoption advantage. Much like relationship between cheap hardware, spread of UNIX, and C compiler availability.

Personally, I can't stand to look at COBOL. I tried to do my part ridding us of it by forwarding things like Semantic Designs Inc to COBOL shops where possible. Very entrenched, though, due to inertia of tons of software written in about a billion lines or so of it in shops where it's what most people know. Sound familiar?

Both C and COBOL are entrenched and (for very different reasons given their areas of applicability) are very difficult to displace. I don't disagree on this, it's an obvious fact.

But COBOL alternatives won. Where COBOL used to rule, Java rules now and .NET languages follow closely. People want to throw COBOL away but can't.

C alternatives just can't get past their niches. Even when people don't have to use C, they choose C. It's more than clear to me that this happens because people like C and actively work to keep it entrenched.

And liking C doesn't mean being unaware of its problems. But it means people want those problems fixed while keeping the core of what they like. I believe C alternatives fail because they go too far. They fix its shortcomings but also go for a different aesthetic.

So yes, my point is largely non-technical. However, our industry is full of examples of things that "won" for largely non-technical reasons.

Fair points. I disagree that all the C alternatives went too far. Even C compilers that just added bounds checks didnt get adoption. The Plan 9 C, with minimal mods, didn't get adoption. C fans won't take fixes in the core language no matter how similar to C. They will and did accept extensions thqt increase its power on occasion.
I remember reading an answer on Quora by a professional C developer why none of existing newly designed programming languages will ever replace C and to be honest with you, I agree with him.

https://www.quora.com/Which-language-has-the-brightest-futur...

If you look beyond the name calling and ad hominems, that entire article can be summarized as "C is popular now, so it will be forever." Which is laughably naive and short-sighted.

Quite possibly the worst defense of C I have ever read in my life. And I like C.

(ps: "professional C developer" doesn't say much. I've worked in embedded systems with C for years, so what? that doesn't make me right. don't be misled by his bloviating and chest thumping; his point is weak.)

OTOH C has been replaced in many of its previous application areas, and the process continues. A lot of the software people now write in JS, Python, Clojure, C# or CUDA would have been written in C 20+ years ago.
I'll review it for you as a C opponent who used C and safe alternatives at various points in time.

1. C is popular. Means nothing for its approach since COBOL, DOS, and VB6 were massively deployed. He'd not endorse them I bet. It won't go away, though, due to socio-economic reasons.

2. C is popular No 2. In embedded with same result as above. Meanwhile, there's embedded projects in subsets of Ada (lots of these), Haskell (Galois Ivory or Atom), Oberon (see Astrobe), BASIC (mikroBASIC), Pascal (mikroPascal), Python (microPython), LISP (L lang/Franz), Ocaml (OcaPIC), and ATS language (8-bitter). A few of these have serious advantages over C. One is niche industry with tens of millions probably spent.

3. C has ecosystem advantages. Definitely true for C and many others. Fortunately, you can use C's ecosystem using what's called a FFI. You can compile to C. So, you can avoid C at your level while benefiting from it. Replace the pieces gradually if you want. Rust is an example of better language trying to integrate well w/ C ecosystem while letting you incrementally rewrite it.

4. "C just works." Propaganda piece in action. C ecosystem is legendary for problems at compile time, build time if 3rd party, and runtime. Even pro's make common mistakes. On other hand, Ada (esp SPARK) and Haskell will tend to run without crashes if you can get them to compile. You have to really work to cause as many problems as average coder runs into with C.

5. "C is, and was designed to be, a “portable assembly language”" This is a myth I demolish in my article above using primary, source material. C was meant to code on three, specific machines with specific philosophies. It inherits their traits in ways that help and harm. It was ported because it was so small & limited. Incidental. An example of portable assembly language is P-code Wirth created to make Pascal portable. He knew writing compilers, standard libraries, OS's, etc was too much for average developer. So, he made an idealized assembler that was super-easy to port. He targeted compiler backend of Pascal/P to generate P-code. They just port P-code then get all the software using same, reference compiler. The result was Pascal was put on 70 architectures in about 2 years varying from 8-bitters to mainframes. I'm still curious what the number and timing was on C ports for comparison.

6. "Your garbage collection, your classes, your libraries and APIs" Alternatives like Modula-2 were about as simple as C just safer, easier to compile, and with less problems. Light, manual C vs heavyweight, OOP, Java or C# is false dilemma. Especially given my list above. Author has done no research in safe, systems languages. I could stop knowing that but I'll be a good sport. :)

7. "C is not a language for programmers that need to be babysat or have their hands held. If you can’t deal with managing your own arrays manually" This kind of counterpoint was originally directed at C itself by assembly and Fortran programmers. It embeds the false assumption that a person with limited time, energy, and focus should spend that on micro-managing a machine because a language designer told them to. If that's unnecessary, that person shouldn't be doing that & it follows C is a waste of time/energy. If it's occasionally necessary, a good language will allow you to optionally do that only where needed. If it's not necessary, a good language will do it for you quite efficiently. A great language will allow all three. Keeping complexity low, I'd say Wikipedia's Modula-3 page illustrates such a language. One neat use was in SPIN OS that could safely load code into a running kernel for acceleration.

So, the author points out popularity has advantages, ignors our ability to use it regardless, pretends C ecosystem is seemless, misinforms about portability, only compares his apple to oranges, and tells people to waste time just because...

... as if all development of C stopped with the first ANSI standard?

C99 and C11 are great languages. VLAs, anonymous structs, variable initializers, atomics... they're not the C of the 80s and 90s. They still have the usual warts but the tooling is great and support is coming along for the newest features to most platforms.

While the spirit of, "trust the programmer," might have been a historical accident there is some merit for its usefulness. Our chief problem as programmers is managing data: taking some source inputs, performing a transformation, and sending the output somewhere. That's true of video games, web applications, embedded sensors, etc. As a high-level language, C is one of the few that gives the programmer some control over how memory is mapped to the target platform and allows them to build the appropriate abstractions on that hardware.

It's also a loaded weapon... humans make mistakes and inevitably those mistakes make it into our programs. When working in a language like C one must conduct ones self accordingly.

I'm a fan of FP and ML-style languages. I'm looking forward to a day when we can have our cake and eat it too. It'd be nice to be able to define how memory should be handled for a particular application at the granularity of the target platform while still getting the safety guarantees.

I like where Blow is going with his Jai language. It'd be neat to see more research in this area.

> C99 and C11 are great languages.

I think the amount of undefined behavior in C--and that undefined behavior is necessary to compensate for its design mistakes (e.g. signed overflow)--disqualifies it from being a "great language". Empirically, virtually every single large C program in existence depends on undefined behavior to continue working. And that UB is very difficult to spot [1].

> It'd be nice to be able to define how memory should be handled for a particular application at the granularity of the target platform while still getting the safety guarantees.

> I like where Blow is going with his Jai language. It'd be neat to see more research in this area.

You said that you wanted safety guarantees, but Jai is explicitly not about safety guarantees. In fact, being memory unsafe is one of the key design criteria behind the language. That's what differentiates it from for example Rust.

[1]: https://news.ycombinator.com/item?id=11728324

I don't disagree wrt. UB. I've read somewhere that the reason C is so portable, despite not being designed that way, might be due to "underspecification." Regardless it's something one must be aware of.

As for Jai, etc... right. I was conflating two things. Jai for keeping the spirit of C wrt "the programmer is in control." The other idea being FP languages with hardware-level capabilities.

> The other idea being FP languages with hardware-level capabilities.

This won't happen until FP implementors work on bridging the gap between typed lambda calculi (ideal source languages) and the C machine model (ideal target machine). This needs:

(0) A limited form of dependent types, tailored to express invariants of data structures, rather than arbitrary mathematical propositions.

(1) Substructural types, so that use-once objects can be reclaimed eagerly and deterministically.

(2) Designing abstractions so that their implementations have no overhead with respect to what a C programmer could have done manually.

But FP people in general don't seem terribly interested in any of these. Instead they write compilers targeting JavaScript.

With my limited experience in Agda I'm really excited about dependent types.

I've been under the impression that there is some research in this area...

A language whose type level allows you to express arbitrary mathematical propositions can't possibly have as good inference as ML and Haskell have, because higher-order unification is known to be undecidable. Agda and Idris require manual type annotations for all let-bound identifiers. Coq requires all dependent pattern-matching blocks to be type-annotated. This is a real chore, and dependent types will never become mainstream unless their usability improves significantly.

What I have in mind is one way to hopefully achieve that “significant usability improvement”:

(0) Have dependent types, but restrict them to be first-order. This is good enough for programming purposes: invariants of data structures can almost always be expressed as first-order formulas.

(1) Use known and proven type inference technology: generate a system of type-level equations, and solve them via first-order unification, which is known to be decidable and tractable in practice.

But I don't see anyone in the FP community exploring this possibility.

I just got a bunch of papers on first-order verification, implementation, compilers, and so on as the simplicity & tractability part peaked my interest. I'm keeping them in case I run into some students or hobbyists that want to try that on top of some "verify the verifier" kind of issues. Then I see these suggestions...

"(0) Have dependent types, but restrict them to be first-order. This is good enough for programming purposes: invariants of data structures can almost always be expressed as first-order formulas.

(1) Use known and proven type inference technology: generate a system of type-level equations, and solve them via first-order unification, which is known to be decidable and tractable in practice."

I'll add those to the list given that sounds worth a good try. Especially if combined with automated approaches to generating & solving for common attributes like in SPARK and AutoCorres. Maybe something will come of it.

> I think the amount of undefined behavior in C--and that undefined behavior is necessary to compensate for its design mistakes (e.g. signed overflow)--disqualifies it from being a "great language".

The undefined behavior is there primarily to account for the variation in platforms. Higher-level languages avoid this by hiding it in a platform-specific compiler or VM, which in turn limits the number of platforms the code can actually be run on.

> Empirically, virtually every single large C program in existence depends on undefined behavior to continue working.

Citation needed. I get that undefined behavior is hard to spot, but having spent years writing cross-platform C I cannot accept that every large program depends on it without some evidence.

> The undefined behavior is there primarily to account for the variation in platforms.

Honest question: Doesn't implementation-defined behavior suffice to address variations across platforms? Undefined behavior has far more drastic consequences: compilers are allowed to assume it simply won't happen.

On further review, I think I was conflating undefined behavior with implementation-defined behavior. As pcwalton pointed out, though, in the early days there wasn't really a distinction between the two.
> The undefined behavior is there primarily to account for the variation in platforms. Higher-level languages avoid this by hiding it in a platform-specific compiler or VM, which in turn limits the number of platforms the code can actually be run on.

It may have started this way in the committee (though that isn't true for e.g. type based alias rules), but nowadays it's depended on for performance. You lose a lot of ability to reason about loop trip count if signed overflow is defined, for example.

> Citation needed. I get that undefined behavior is hard to spot, but having spent years writing cross-platform C I cannot accept that every large program depends on it without some evidence.

The probability that a program depends on any of (nonexhaustive list) (a) inspecting the value of a dangling pointer; (b) taking the offset from NULL; (c) type-punning through a union; (d) type-punning through any type other than char; (e) signed overflow; (f) order of operations between sequence points quickly approaches 1.

Here's a very interesting survey investigating this: https://www.cl.cam.ac.uk/~pes20/cerberus/notes50-survey-disc...

> It may have started this way in the committee (though that isn't true for e.g. type based alias rules), but nowadays it's depended on for performance. You lose a lot of ability to reason about loop trip count if signed overflow is defined, for example.

I agree it is depended on in many cases, and should not be. That is on the programmers though, not the language designers. The behavior does not exist to cover deficiencies in the language but those of its users.

> The probability that a program depends on any of (nonexhaustive list) (a) inspecting the value of a dangling pointer; (b) taking the offset from NULL; (c) type-punning through a union; (d) type-punning through any type other than char; (e) signed overflow; (f) order of operations between sequence points quickly approaches 1.

Just on your short list I can tell you the probably of any of our C code depending on that approached zero:

> inspecting the value of a dangling pointer

Our code was malloc once, free never. Dangling pointers were by definition impossible.

> taking the offset from NULL > type-punning through a union > type-punning through any type other than char > signed overflow > order of operations between sequence points

Our coding standards covered all of those, so their existence would be a code review failure.

We weren't specifically writing to MISRA C, but I will wager that your statement is not accurate for any project that follows it or any similar standard. Yes, it is a lot of work that you don't need to do with a more controlled and less flexible language. I'm not going to challenge that. I'm just challenging the idea that nobody is writing C that avoids undefined behavior.

> Our code was malloc once, free never. Dangling pointers were by definition impossible.

Dangling pointers can arise in other ways: use an uninitialized pointer variable, take the wrong offset from an otherwise valid pointer, etc.

Those are indeterminate values but they aren't dangling pointers. Dangling pointers are specifically references to an object that has been freed.

Plus, the cases you highlighted are programming errors, not reliance on undefined behavior.

(a) how program can depend on inspecting the value of a dangling pointer? I can't think of it as not a bug.

(b) True. I think this one should be defined and standardized.

(c) It seems that C99 defined this behaviour. https://stackoverflow.com/questions/11639947/is-type-punning...

(d) I am not knowledgeable enough. Could you give an example?

Higher-level languages avoid this by hiding it in a platform-specific compiler or VM, which in turn limits the number of platforms the code can actually be run on.

False. Squeak Smalltalk runs with a bit-identical model on a ridiculous number of platforms. (which is to say OS/ISA combinations) Though to be fair, it accomplished this trick by having the VM written in a Smalltalk subset that gets translated into C, which is then compiled by local compilers.

Citation needed. I get that undefined behavior is hard to spot, but having spent years writing cross-platform C I cannot accept that every large program depends on it without some evidence.

I'm not finding an academic citation, but perhaps a respected academic's blog counts? I think John Regehr is the likely source of these claims. In his words:

  My guess is that most or all executions of GCC
  and LLVM (and basically all other large programs)
  would be undefined.  
http://blog.regehr.org/archives/761

He starts the post by stating that the "majority of the programs in SPEC CINT 2006 execute undefined behavior during the course of a 'reportable' run". A majority is definitely not "all", but the SPEC programs are also not "large":

Let’s consider the SPEC benchmark programs. Making them fast is something that most compiler vendors would like to do. Using IOC we found that a majority of the programs in SPEC CINT 2006 execute undefined behavior during the course of a “reportable” run.

For large programs, he then looks at the GCC and Clang compilers themselves, and concludes:

If we consider them to be written in C99, LLVM/Clang 3.1 and GCC (SVN head from July 14 2012), running on Linux on x86-64, do not appear to admit any non-trivial, non-erroneous executions. I expect the same is true of most or all previous versions of these compilers. IOC tells us that these compilers execute undefined behaviors even when compiling an empty C or C++ program with optimizations turned off.

Lest you think that the undefined behavior is caused by inappropriately forcing C99 on earlier code, he goes on to explain that the problems probably exist there too, but that the lack of tooling for earlier dialects prevents proper analysis:

My guess is that most or all executions of GCC and LLVM (and basically all other large programs) would be undefined by C89 (and not just C99) but we can’t tell for sure without using a comprehensive undefined behavior checker for C89. This tool does not exist. I confess to not personally having the gumption necessary for cramming GCC or LLVM through the best available dynamic undefined behavior checkers: KCC and Frama-C.

His overall message (which I think is true and merits significant reflection) is that C's utility as a real world language is based on unspoken agreements with compiler writers to use some degree of judgment and restraint when making optimizations based on undefined behavior. Rather than a standardized language defined by the written specifications, we are dependent on an "undocumented dialect of C where undefined behavior can sometimes be exploited and sometimes not":

This means that a correct C/C++ compiler could generate code that is almost infinitely faster than the code generated by current compilers. The SPEC harness would of course complain that the compiled programs produce the wrong results, but this is actually a bug in the SPEC harness, not a bug in the compilers. If we insist that a compiler turns these SPEC programs into executables that produce the specified results, then we are effectively mandating that this compiler implements a bizarre, undocumented dialect of C where undefined behavior can sometimes be exploited and sometimes not.

http://blog.regehr.org/archives/761

> ... as if all development of C stopped with the first ANSI standard?

For a significant portion of C developers and target platforms, it did. On my last program at my previous company I was required to use C89, in 2012, for portability reasons. The justification was political, not technical.

"... as if all development of C stopped with the first ANSI standard?"

The purpose of the text was to trace why the specific problems in C exist. Its proponents claimed all sorts of justifications and design tradeoffs. After digging through source papers, I found most of these were myths with two, real justifications: hardware constraints & personal preferences. What happened latter on in C didn't matter as core problems were due to the EDSAC, PDP-7, and PDP-11 hardware constraints plus personal tradeoffs of authors. The point is that (a) it wasn't actually designed that way for a good reason (just chopped due to constraints), (b) the possibly good justifications for chopping safety/maintenance no longer applied with better hardware, and (c) people should stop repeating misinformation that there was some grander reason they were there so we can adapt language to new constraints. We might get more improvements on C itself or alternatives as more C users understand there is no grand reason behind the root causes of problems: only due to constraints on hardware you can't buy any more.

"They still have the usual warts but the tooling is great and support is coming along for the newest features to most platforms."

C is bad at the foundation. Alternatives, esp variants on Wirth's stuff, was better within a decade or so. By then, people should be building on better foundations & improving C's. Some even did but little uptake over inertia & backward compatibility. Social & economic thing, not technical.

"As a high-level language, C is one of the few that gives the programmer some control over how memory is mapped to the target platform and allows them to build the appropriate abstractions on that hardware."

There's been safer alternatives that could do that at various points. I'll consider including them in a future version.

" humans make mistakes and inevitably those mistakes make it into our programs. When working in a language like C one must conduct ones self accordingly."

This is sort of a strawman that C proponents critically depend on. C makes many common operations unsafe by default where alternatives, esp like Modula-2, make it safer by default with ability to go UNSAFE if performance or low-level absolutely depend on it with that contained in a module. The reason these problems are in C language by default is nobody changed them. The reason they entered the language early on are the people who invented that philosophy & tech in BCPL had an EDSAC, next had a PDP-7, next had a PDP-11, and none of the three tried a safe subset of ALGOL like Hansen & Wirth did. Both of those built on PDP-11's. Hardware constraints, personal preferences, and not caring about safety/maintenance. That's it.

Once you know this, there's no reason on modern hardware to do a clean-slate project in C if usable alternatives exist. Then someone mentions libraries or compilers. Many safe languages do a strong FFI for C or can compile to equivalent C code. So you still don't need C's problems at level you work with. Then someone mentions talent availability for C. The funny thing about that is that there's very little talent that can write robust, C apps esp under pressure of management to ship fast and including poorly-documented, 3rd-party libs. Only good counter-argument I've seen for using C was, ironically, in safety-critical from a NASA guy pointing out their people with years of C coding using a C subset w/ C testing rigs and four, static analyzers were collectively better bet than clean-slate language w/ less tooling on a project where absolutely everything has to be right. That was true but not the typical situation. ;)

"It'd be nice to be able to define how memory should be handled for a particular application at the granularity of the target platform while still getting the safety guarantees."

Stuff like that already exists and non-experts rewro...

Myreen et al can go from specs to machine code directly with verified translation, which seL4 used IIRC.

His special method is to meet in the middle, to analyze the binary code and "turn it into logic" and then check that against the higher level spec. Or something to that effect.

They tried CompCert but there was evidently too great an impedance mismatch. Good thing, too, since CompCert isn't free for most uses.

Basically. The good news is CompCert might not be needed. Remember that seL4 bypassed need for CompCert by somehow translating it directly to ASM. I found out they used Myreen et al's method.

https://www.cl.cam.ac.uk/~mom22/stacks.html

Their work is mindblowing. There's almost no TCB left lol. I still want diverse verifiers in diverse logics and machine codes to verify the HOL checkers and such. I don't trust their verify it in itself sort of method. Not entirely. They do have a nice projects page for CakeML listing each improvement that would be useful and skill estimate. More should do that.

Look at NICTA's COGENT, too. It's open-source now with an ext2 implementation partly or mostly done.

https://ts.data61.csiro.au/projects/TS/cogent.pml

Here's the Simpl language I referenced that's I believe at the heart of AutoCorres tool. They embedded C in it but it's not C specific.

https://www.isa-afp.org/entries/Simpl.shtml

Thanks for this amazing reply. If only you, catnaroek, and I were able to sit down over coffee for an hour or two.

This has been a fascinating thread.

That would be fun. The thread has been more interesting than I thought it would be. Probably thanks to Tomte changing the title haha. We'll all have to have that coffee sometime.
C is horrible but Algol60, CPL, and Algol68 were wonderful? Um... sure. (Backs away slowly...)

This reeks of ideological blindness, of having reached a conclusion and then throwing mud to try to support it, no matter how biased a twist on the facts it takes.

EDIT: I got lost in the comment thread. I was intending to reply to this article: https://www.quora.com/Which-language-has-the-brightest-futur...

Do Go, Rust, or D programmers really fantasize about their language replacing C in any other context than their own work? The article setups up a huge strawman of a caricature of a person to start and then uses really rather spurious arguments to take pot-shots at that strawman.

But just some easy ones, right off the bat:

"C is not a language for programmers that need to be babysat"

I think the number of buffer overflow exploits that we have seen in the wild over the years indicates that C programmers really do need to be babysat. If you're starting a project and you say "let's do it in C", the rest of your team needs to be taking a really hard look at you and asking, "and just who do you think you are?"

I have never met anyone who knows how to program in C "correctly", because those that have opinions on how it's done always seem to be at odds with some other cohort that is equally convinced of their own superiority.

It's possible to write perfectly correct[1] code in 'C'. It's just that some people can't be bothered, and they can't be bothered because it's somewhat tedious.

[1] not to C.A.R. Tony Hoare levels of "provably correct" , the other sort of correct.

Pre-open source, I never saw buffer overflows. When I was dealing with full time, professional 'C' coders, this didn't happen. Because we all talked to each other and taught each other how to not do that. It's like any other skill or bundle of habits.

I did see the odd "signed integer going negative" or other integer overflow problem, but that was much more about broken analysis and flawed testing. Yeah, it took a little more time to make sure this was the case, but it was just required.

ArkyBeagle must have worked with some talented people. Most of the programmers, sloppy or rushed, made enough of specific errors they became the top 10. This included proprietary systems given Microsoft was in the lead on bug-count due to bugs in product and market share (i.e. targeting). Their choice was a combo of stronger review of unsafe code, introducing managed code to get rid of it everywhere possible, and huge investments in various, formal verifications. Their 0-day rate very low these days to point attackers hit 3rd party applications, usually also in C or C++. They said it cost many millions to do that bug-hunting, though.

Might be cheaper for next shop to just pick a language that's easier to do correctly. Someone giving that hard look as you stated saying, "Bro, have you even heard of Ada, Go, or Rust? And you still want to suffer through making a robust app in C? Why!?"

The author snorts at aspects of C that led to its commonplace usage.

> 9. New design goal: trim out anything hard to compile from CPL. Naturally eliminates most features for robust and maintainable programming.

Naturally? If those features are hard to compile, then they are hard to reason about; the author begs the question in their statement, but I am not convinced by their conclusion. There's a tendency for things which are hard to reason about to be difficult to port to other systems; it is a complexity issue, and the complexity of a behaviour influences how it can be re-implemented easily or swiftly by others.

> 11. The result of these was BCPL [5]: a typeless, word-oriented language with few keywords and unrestricted use of memory. Created philosophy of "the programmer is in charge and gets no help." Compiler was easy to write on Most Godawful Computer on Earth. Ran fast on it, too.

Great!

Sounds like BCPL was an excellent tool for their use case, and in providing power with a low difficulty for implementation, it would be easy for others to enjoy its benefits.

> 14. [...] Stripped out or changed most features benefiting safety/security, maintenance, and consistency.

Because they were unnecessary. Don't over-engineer your products, or you'll end up with a product that fails in the market; like MULTICS. It's like they learned a thing or two from their past ventures.

My Conclusion: C and Unix succeeded because they were simple to re-implement and/or port. Other, more complex systems were too hard or near-impossible to re-implement and/or port. At a time when good, cheap software was in-demand but scarce the pair were well-positioned to sweep the market, and so they did. Unix may have failed to reach the desktop, but C made it there; because it was easy to implement, had a small foot print, and produced fast code.

It's not like C and Unix stopped development, either. Let's not ignore the last 30 years, now.

Recursion today is easy to compile, but it's something that is hard to support on some older architectures. For instance, you would have a hard time (if not outright impossible) supporting recursion on a PDP-8, which stored return addresses at a fixed location (so subroutines are inherently non-reentrant).

For a fun time, try implementing a C compiler for 6502. 256 byte stack, 8 bit index registers, single accumulator, 64K RAM max (which only became affordable in the mid 80s). Those are the types of constraints compiler writers were facing at the time.

"Features" weren't hard to compile because they were difficult to reason about, they were hard to compile due to physical constraints of the hardware.

"Naturally? If those features are hard to compile, then they are hard to reason about"

"Sounds like BCPL was an excellent tool for their use case, and in providing power with a low difficulty for implementation"

This is supposed to be a quick, digestible history. Your statement shows I might have chopped it too much myself. :) Work like Wirths...

https://news.ycombinator.com/item?id=12316092

...showed you can have a great baseline that trades against many somewhat-contradicting criteria without much difficulty in compiler. Something better was definitely doable, esp when C was being made. We can't know exactly what was in BCPL author's head as he and his team chopped this feature vs that feature of their ALGOL. What I do know is the constraint they had, which we don't, is the EDSAC they worked with. Here's you a great video of how glorious software development was for these people:

https://youtu.be/6v4Juzn10gM?t=3m41s

This computer was a glorified calculator. It had basically no processing or storage capability as we'd think of them today. They interacted with it using program tapes that were manually created. Everyone in building queued up to use and share it. The iteration time was long and increments had to be short. The developments were likely done on paper by a team who contributed to what would eventually be on the tape submissions. Truly a nightmare for someone trying to build an ALGOL or even a GCC for that matter. Hence, Most Godawful Computer on Earth. :)

It was that computer's limitations, in development time & running speed, that forced them to chop off features they knew were important. They probably had no choice. Later on, with a PDP-11, Wirth et al showed there was a choice for key features on better hardware. Thompson really liked BCPL... personal preference... since he used it on MULTICS project & toy projects. Wanted some MULTICS capabilities minus scope & hardware cost.They modified BCPL because he liked its style to put a slim, unsafe variant of MULTICS on a PDP-7 and PDP-11 because it couldn't fit OS safety features. Hardware weaknesses also crept into the stack direction, IIRC null pointer issue, bin vs sbin, and so on.

Once you know all this, you can make a more informed decision about how to improve C or avoid it. You can say, "Am I using an EDSAC-equivalent to build a compiler? Yes? Then BCPL or C maybe. If not, then go for default safety & modularity of Modula-2/3 given your job at OS & app levels are going to be way easier outside lowest layers." For OS, you might say, "Would it be easier to code the OS like any other application a la Wirth's Oberon with checks disabled for slow hardware and enabled for good hardware? Or should we just get rid of them permanently in a way that will require breaking apps later to improve?" UNIX went with latter but many others did former successfully. So, we know it can be done.

Stuff like that comes from knowing better models existed even for limited hardware + many design decisions were hardware constraints or personal preferences. And our current constraints/goals are different. So, the language should be. Gotta know the history to see that clearly. Lots of misinformation out there instead.

"Don't over-engineer your products, or you'll end up with a product that fails in the market; like MULTICS."

Or engineer it well to get those that dominate markets. Examples include Burroughs (Unisys) & IBM mainframes, the AS/400, OpenVMS during minicomputer era, the huge UNIXen (incl GNU/Linux) dominating servers, and the desktop clone/mod of OpenVMS Cutler did for Microsoft that had most desktop market-share last I checked. iPhone, opposite of simple platforms predat...

Don't over-engineer your products, or you'll end up with a product that fails in the market; like MULTICS.

While it might have failed in the long term anyway, Multics didn't fail because of market reasons. It failed because first, Honeywell was incompetent at developing serious hardware, e.g. officially blaming the failure of the first (and only, I guess) clean slate Multics CPU project on the decision to microcode the machine. Something which IBM had previously used in the System/360 to dominate the market....

That caused more and more problems as its async CPUs fell further and further behind, although this was still in the discrete logic days, and you could easily use up to 6 in a configuration, and more with a kludge. But it was still selling well, especially in France, where Bull seriously pushed it.

Then it was deliberately killed off due to internal politics, it seems that trying to smash together three computer companies, Honeywell's original stuff, GE's including Multics, and SDS -> Xerox -> Honeywell, creates a situation where killing off rival systems makes sense to political winners who just might not be technological winners.

That Multics was doomed by all this was self-evident to me in the 1979, one reason I never spent any serious time on it.

Interesting. I didn't hear that angle before. So, you don't think cost of like $7 million for probably less performance wasn't going to end it? Far as rivals part, that same thing happened once HP had both NonStop and OpenVMS. One had to go. Whereas Unisys did it on purpose when it acquired CTOS, a distributed mainframe. They weren't taking any chances on that one haha.
It all depends on what you want to do with it. Crunch numbers? No way after some point. Prepare the Air Force's annual budget submission without information leaking? A real example of a perfect fit. Or provide the IT for MIT's EECS department? Good fit, until MIT-MULTICS was shut down as part of Honeywell killing Multics and the Cambridge lab which did work on it (I played a role in moving off that).
In a world that is overflowing with competing and incompatible operating systems, it's the most portable and fast language that wins over the hearts of those writing software.
I imagined this being read by one of the Honest Trailer guys.
The OS won due to a number of factors. The language came with it. Then, as the OS spread, the compilers for its source language spread. Then that language was everywhere. With that OS and language everywhere, esp on cheap machines, ecosystem effect kicks in from there where people start building on, using, or targeting it. Most portable and fast language isn't why it succeeded as UNIX could've been built with alternatives. UNIX itself was the killer app that caused C to flourish.
I assume the author meant Bjarne Stroustrup for items 20 and 21. It's spelled wrong twice, as Bjourne and Bjourn.

> Conclusion 3: C and UNIX should be avoided where possible because they're Bad by Design for reasons that stopped applying sometime in the 80's or early 90's.

What does the author propose instead? Is there a major OS that has a kernel not written in C?

And, "designed to deal with a certain set of limitations" != "Bad by Design". The latter implies malice.

The author seems to wish that Algol had won instead. I can't imagine that the level of complaining would be less in that hypothetical world...

" I can't imagine that the level of complaining would be less in that hypothetical world..."

We don't need to hypothesize as Wirth and Hansen took the path of subsetting and improving ALGOL instead of BCPL. We get to see the result. :) The C alternative would've been a subset of ALGOL like Modula-2 or something. Gradually-improving type-safety, memory-safety by default, option to remove checks for performance, modules, less undef behavior, better linking, easier analysis for optimization, and quicker compile/test cycles. Having coded performance-sensitive applications in languages like that and C tells me the level of complaining would be much lower if it's in any way tied to number of vulnerabilities, time spent debugging, or ease of modifying legacy systems without major breaks.

It's also not hypothetical: all kinds of developers use such languages. C is the exception to the rule these days. Even in UNIX & C's domains, there's new comers kicking the crap out of it in terms of balancing development speed, production speed, safety, and extensibility. Redox in Rust comes to mind given the OS and language attempt a Right Thing approach to bootstrapping an OS with small team. Progress has been impressive.

Perhaps the IBM operating systems? https://en.wikipedia.org/wiki/IBM_zEnterprise_System#Operati... . I can't tell what they are written in. Their heritage extends to before C existed.

It also depends on where your major/minor cutoff is located.

Oh true forgot about mainframe things. It also looks like VMS has other languages than C: https://en.wikipedia.org/wiki/OpenVMS

I don't really know where the major/minor cutoff is myself. :)

Pretty much every OS I can think of is in C - Windows, *nix, RTEMS, INTEGRITY, VxWorks

Remember that a few of those RTOS's, although OS itself is C, have runtimes for Ada and Java that apparently sold well enough they continue to be updated. Only RTOS I know still done in Ada is MaRTE OS:

http://marte.unican.es/

Well, Muen is done in SPARK but it's a separation kernel rather than full OS:

https://muen.sk/#_features

That it was integrated into GenodeOS framework shows one could build a system around it but it's not quite one itself. There's also the JavaCard OS's for low-level stuff and even Java CPU's on market.

PL/I, PL/S variants, macro assembly (eg HLASM), assembly, and microcode that I can tell. PL/S was an interesting language that was high-level, not GC'd, and gave programmers control over how each function compiled. Pretty powerful for 1968. See BSL specification PDF in references section.

https://en.wikipedia.org/wiki/IBM_PL/S

"I assume the author meant Bjarne Stroustrup for items 20 and 21. It's spelled wrong twice, as Bjourne and Bjourn."

Thanks for the catch. No telling how many ways I've messed his name up.

"What does the author propose instead? Is there a major OS that has a kernel not written in C?"

There's a lot of languages you can use in place of C that are fast, safer, can do low-level stuff, and sometimes compile faster. Any of Wirth's are an example. Modula-3 was cleanest at negotiating among many tradeoffs, Modula-2 being one of simplest built on PDP-11, & FreePASCAL well-supported on many architectures. Ada w/ SPARK is the most thorough in removing defects but steepest learning curve. C++, coded like C++ not C, has significant advantages over C with Ironclad C++ adding lots of safety. Its unofficial, successor D language is even better esp w/ SafeD subset. Rust is promising with many advantages over C, strong integration w/ C apps, and temporal/concurrency safety features. PreScheme, in Scheme48, was a fast, simple, low-level LISP that compiled to C or ASM if you modified it. Advantages of C and Scheme but no GC. COGENT at NICTA is functional language for easier, formal verification plus verified compilation to C. Amateurs rewrote ext2 in it successfully. There's a few.

Far as OS's, eh it's more about how we should incrementally re-write or improve them as OS market is a done deal. Components are simply too huge to replace, although embedded has potential. There were OS's not written in C. Examples include ultra-reliable AS/400 (used PL/S), OpenVMS (used BLISS & macros), Oberon System (A2 Bluebottle is latest), Edison System on PDP-11, many in C++, a few in Ada (including OSS Martes), JX OS in Java, and (recent/active) Redox in Rust. Lots of academic or hobbyist stuff including Mesa (Xerox Palo Alto language inspired Modula's/Java), COSMOS (C#), JNode (Java), House (Haskell), and LISP machine clones. It can definitely be done and gradually if language is designed for clean integration with C codebases.

I think a large problem with this is that people unreasonably idealize the features of ALGOL 60 and many other "Perfect Systems Languages"

The issue is that there are not any major open source projects to examine that use these languages to see how they actually work in the wild. For example, writing a Java codebase with nothing other then the standard library you can do some very nice design patterns. You can come up with some very clean and concise code and everything can be perfect. Then, when you go to integrate with someone else's code (see Spring) you notice that everything becomes a mess since they are using some crazy things that make it very difficult to integrate with it.

Due to this it's impossible to say how well another language would behave in the wild since I don't think anyone can predict how well the language will be used to it's extent.

That being said, I think C succeeded because it was SIMPLE. Not only for the compiler, but I can teach pretty much anyone how to understand C in most likely a few days to a week. It's very simple and very little you need to know to understand how to start writing your programs.

On the other hand, ALGOL is MASSIVE. There are 106 reserved identifiers in the language according to wikipedia and I don't think I could ever remember what every one of them did.

Currently C has about 32 key words and I can tell you in full detail what every one of them means except union as I don't work much with C.

This is good for the beginning programmer since it shortens the time they need to hit the books and helps them jump strait into writing.

Something that's good for the advanced programmer, in my opinion, is the use of braces and not words.

I know this is kind of controversial but I do honestly think that a code should be easily segmented by the eye not readable.

Begin and End only help so much and by so much I mean not at all. Words should not be syntax in my opinion. Seeing { } is less to look at and understand then seing Begin and End.

This sample phenomenon can be looked at from the recent Linux 1000 patches debacle.

Linus said he didn't want to replace unix file permission numbers with the symbolic values. Why? Because it's much much faster for someone to see the numbers and know what's going on then to have to read a bunch of text. It's much quicker in parsing.

That's where the breakdown comes in these discussions. People will throw "You've never used these, how do you know yours is faster."

While that's true that doesn't really present a good argument for changing everything.

I also hate needing to type := for assignment. It makes no sense. Fixing the beginner issue of missing the second equal sign in an if statement has a much simpler fix. Use some kind of IDE/Editor with a linter. You'll not have this problem.

> "Currently C has about 32 key words"

That was in C89. C99 and C11 added 5 and 7 more, respectively, bringing the count to 44 in modern C.

I stand corrected then, make the number 44.
> I can teach pretty much anyone how to understand C in most likely a few days to a week.

I learned C from reading K&R over a Thanksgiving weekend, without even a compiler to try it out on. The only thing that confused me was the explanation of argc and argv.

(comment deleted)
I learned an industrial BASIC in a few hours if you add it all up. Learning how to use it reliably took weeks with safety mostly coming by default. Learning that for large stuff took several months where I learned programming practice. How long do these milestones take for average C developer with what productivity? Note that my Pentium 3 at 400MHz could compile the compiler in a second or two. Fast iterations. :)

Learning C was a different experience entirely. The mental work involved was huge with the level I worked at for any job being micro-managing the machine. It's only simple on the surface. The job is much harder than a BASIC, Oberon, or whatever with selective use of unsafe features.

I said that I understood the language after reading K&R. The language is simple. Using it safely? Not so simple. I didn't claim that it was.

On the other hand, I learned BASIC on a TRS-80. Safe? Not with the POKE command being part of the language.

"On the other hand, ALGOL is MASSIVE. There are 106 reserved identifiers in the language according to wikipedia and I don't think I could ever remember what every one of them did."

You start with what's shown to have benefits. You then subset it to work within your constraints. I described an example of what C inventors might have done here:

https://news.ycombinator.com/item?id=12316092

"Due to this it's impossible to say how well another language would behave in the wild since I don't think anyone can predict how well the language will be used to it's extent."

Better if it was designed to do that. As I illustrated, C wasn't: portability was incidental. The languages designed to make portability, safety, integration, etc first-class features handle the "in the wild" effect way better. Of the simple ones, Wirth's keep things consistent by focusing on types, memory-safety where possible, interface checks, concurrency support, and modularity. Of older days, Ada is probably one that most systematically approached the problems of programming. Due to all its interface safety & support for cross-language development, the integrations and extensions of Ada programs by multiple developers usually happened with fewer problems than you'd expect.

So, you can design such capabilities in. The resulting problems will be smaller. C didn't. The problems were large and seemingly impossible to fix afterward. Makes sense.

"Something that's good for the advanced programmer, in my opinion, is the use of braces and not words."

I'm sticking to the concrete stuff. Much of the syntax stuff is opinion which hasn't been empirically tested enough. Quorum is closest to that. Except for one that had some evidence:

"I also hate needing to type := for assignment. It makes no sense. "

The language has to support assignment and equality. Originally, that was := (arbitrary) for assignment and = for equality. The change was to = for assignment and == for equality. That's less intuitive. Led to errors. Also, "change your IDE/tooling" doesn't cover for bad language design. It's also exactly what I'm telling people to do in the article: ditch C because it's bad design wherever possible.

@ Tomte

Thanks for posting it as the comments have valuable feedback. I was holding off on responses until it dropped off the front page to let the discussions happen unimpeded. I can already see ways to make the case more clear. What I haven't seen is any contradictory evidence supplied with citations about (a) why C's justifications then no longer apply outside maybe embedded or (b) why it was better a design level than others. A few, good opinions, though. A few comments also have some misinformation on C the article itself addresses despite citations in article... from inventors of BCPL and C... directly contradicting their claims. That's the psychological effect I mention where cause and effect being so far separated in history that affected people not knowing bad causes (or no longer applicable ones) start inventing better, false causes to reinforce their choice of language or faith in its design process.

All in all, I've enjoyed reading the feedback on my ranty summary of that Vimeo vid I did in half an hour or so after doing a C debate. I did condense hours of watching/reading into something digestible but presentation is still shit in a number of ways. Many of points survived with others needing to be improved or replaced to make case clearer. Will do in near future. :)