I feel like I'm always trying to make this point to people, and doing so with far less eloquence. The fact that Katz is (a) an experienced and accomplished developer and (b) someone with very non-trivial experience with hot, hip, super-high-level languages should not go unnoticed. He's not some cranky old embedded systems programmer; he's doing thoroughly modern development.
Someone the other day said that the great thing about C is that you can get your head around it. When Java, or C++, or Erlang, or Common Lisp, or Haskell programming becomes unbearable, it's almost always because the chain of abstractions overwhelms the person building the system. And to be honest, it's why I'm a little less enthusiastic than most of the people here about Rust.
> When Java, or C++, or Erlang, or Common Lisp, or Haskell programming becomes unbearable, it's almost always because the chain of abstractions overwhelms the person building the system.
I ran into a bug in an XOrg driver for an old r128 card a few years back. (I think I have that combination right.) As I remember it, I spent several hours tracing through layer upon layer of C macros to even begin to get an idea of what the code was doing. The bug turned out to be part of a structure getting overwritten clobbering a part of a vertex definition with a color value due to a mix up in the size of the structure. (I'm forgetting the details on this and I can't remember the bug number off hand or I'd look it up.)
In any event, those layers of macros made it almost impossible to trace what the heck the code was doing without intimate knowledge of every part layer involved. I'd say chains of abstraction are more a feature of programming style rather than the language itself. Albeit, the language can ease the load a little.
The region system is obviously a source of complexity. But I feel as though sacrificing safety at compile time in the name of simplicity just leads to complexity later—you either pay the complexity tax at compile time, or you transfer the complexity of correct memory management to runtime, resulting in sessions sitting in front of gdb or valgrind.
I can't really disagree with what you're saying. But I take the point of the article to be that what makes development easy or hard is not as related to the feature set of the language as we think.
The common liturgy of language design is that x feature makes development easier, less error prone, safer, etc. Nearly always, the precise way in which x feature does this is as clear as a bell. But somehow, as we multiply the number of features, something truly destructive starts to happen. It's not just diminishing returns . . .
I think about a language like C++. I can see the reasons for every single thing in it. I can understand the arguments for why this or that is a good thing. But it is perilously easy to write very unmaintainable code in this language, and to understand why, I think we have to get beyond the usual debates (static vs. dynamic, functional vs. OO, compiled vs. interpreted, etc). Small vs. large might be one place to start.
Can you give me an example of a problem in Haskell, where the abstractions are so complicated you can't get your head around it, but putting it back in C makes it easy enough to deal with?
A more plausible explanation is that you wouldn't even attempt a similar design in C because it'd be obviously impossible.
An industrial operating system. In C, you know what you've got: its exact dimensions, location, and lifetime. Since you're not relying on a GC, you can make latency more predictable and with much lower upper bounds.
On that topic, I'd rather write crypto code in C too.
Someone the other day said that the great thing about C is that you can get your head around it. When Java, or C++, or Erlang, or Common Lisp, or Haskell programming becomes unbearable, it's almost always because the chain of abstractions overwhelms the person building the system. And to be honest, it's why I'm a little less enthusiastic than most of the people here about Rust.