C Ain't Dead

I often hear people lamenting that C is little better than macro assembler. From a lot of perspectives they are correct. A great example of a powerful macro assembler is High-Level Assembler for the mainframe (mainframe, like OS/360). HLASM has a very long history of having a powerful macro language. Today programming in HLASM is not really so different from programming in C. They have macros for modernized function linkage (including recursion-friendly function calling and run-time dynamic linking) and the language even has built-in support for structs (called "DSECTs").

What's missing from HLASM that's in C? HLASM doesn't have a register allocator -- if your function has more variables than you have registers, you're going to have to be clever. HLASM doesn't have a decent type system (but, well, C's isn't that much better). HLASM isn't portable at all (Switching to Unix? Pay some guy like me dollars per line to convert your code to C). All of these things matter a great deal in our day to day lives, but they don't really change the way we think about programming in an abstract sense. It's still procedural programming close to the hardware.

Yet when you show a smart C programmer XML he yawns, and when you show a smart HLASM programmer XML he thinks of it as totally alien. What's the deal here? For a long time I thought that there was no such thing as a smart HLASM programmer, and I also thought that XML was overblown blah. But then I met a smart HLASM programmer and I realized XML is absolutely awesome because it's just the most popular way to introduce HLASM programmers to things C programmers take for granted.

The difference is a generational difference. Because I am a C programmer, I was introduced to recursion at a very young age. I remember being quite impressed with myself when I wrote my first recursive descent parser in my teenage years. It was for the following syntax:

expr: val + val | val - val | val * val | val / val | val
val: number | variable | ( expr )
To me this is very simple (like XML), but it wouldn't be if I hadn't been there when I was young. To HLASM programmers, really convenient recursion-capable function calls are just an inefficient novelty. As such, recursive descent parsers are an obscure miracle.

So the question is, what is the next generation of programmers going to be able to take for granted that the current generation of C programmers cannot wrap their mind around? There are lots of possibilities. Object-oriented and functional/descriptive programming come to mind. So why haven't these ideas found footing in some modern language and spawned a generation of programmers who are smarter than us?

Let's address OOP first. I actually did grow up in an OOP world. Of course I used C++ a lot in my youth, but my real OOP exposure comes from C, BASIC, and Java. VisualBasic 3.0 for Windows 3.1 turned me on to the idea that OOP is a good way to organize code for a GUI. Java AWT really made programatic OOP-style definition of a GUI practical for me. Today I use GTK in C for the same end. I'm sure OOP is better for some things other than GUIs, and I've even used it in a lot of other contexts, but I've never really found it to give much of a "killer app" sensation except for GUIs. But I suppose it is fair to say that I can't really imagine someone who wasn't exposed to OOP at a young age ever really "getting" GUI application development. So maybe this is our killer app? Think again. I have not found practicing OOP within a C context to slow me down any. It can sometimes be a little cumbersome, but not compared to by-hand register allocating in HLASM...

So what about functional programming? Well, what about it? It's clear that functional programmers can solve problems in neat ways that the rest of us can't. Not only is it difficult to implement these functional solutions in C, but it's pretty much unheard of for a C programmer to even understand the problem statement. On the other hand, most of the problems they solve are problems they invented. So....no killer app yet.

What's your next great thing?

Update 2020/03/11

A few things come to mind about 10 years later...

I want to share this intrusion of functional programming into the mainstream: map -- apply a function over each element of an array. If you're going to post a one-line python example, you better use map at least once or no one will be able to tell how smart you are. Come on guys! 99.9% of the time it's just replacing a for loop. It's not actually any more expressive. In the rare case where the underlying type is too complicated to iterate over with a for loop, odds are pretty good that you'll have very specific requirements and need to do it manually anyways. I'm not opposed to map, it's just an improved dialect for speaking the same old procedural language.

Or maybe I do hate map. The underlying theory, the thing connecting it to functional programming, is the lack of side effects. If your function returns a new version of the element it is given, then map produces a new copy of the array instead of side-effecting into the old one. But in practice, nearly everyone uses map specifically for side-effects, either to modify the array or to convert it into I/O operations. Python code is invariably poorly-performing, and I suspect this may be a part of it. In principle, you can adopt a style that doesn't overburden the memory allocator, but the "I'm so smart because I used map!" fetishization leads people into the opposite style.

Also, more and more languages these days -- such as C++11 -- are supporting lambda expressions with captured local variables. It seems to me the most common use for this is (finally!) a standard way to write statement expressions:

[]{ statement; return value; }()
and not anything actually neato. In Java, it looks to be the same story: lambda syntax is a (much) more convenient way to provide in-line function definitions for continuation passing style or event handling or so on. It doesn't look like anyone really writes different code, they just express the same old code using a shorter syntax.

One thing that has become dominant is the idea of integrating everything into a language. Rust, for example, isn't just a language, it's also a specific ecosystem integrating the compiler, debugger, build system, and dependencies. I hate it:

Android apps are a good example of the downsides of this development ideology. I often disassemble (baksmali) Android apps for one reason or another, and almost all of them pull in vast libraries (hundreds of thousands of lines of code with megabytes of associated resource files) just to perform a task that would only take about a hundred lines of code to do by hand. I've written a number of non-bloated Android apps, which proves the problem can be overcome but also puts me in conflict with Google's process for updating (breaking) the build environment. I've literally had cases where Google Play Store has forced me to upgrade and then I get user complaints from the bugs in the new version of the Android SDK itself. It can't actually be 100% mitigated, because the integration is that thorough, extending through to the store caring about what SDK you used.