Zenhack.net

Three Funerals In The Name Of Clarity (Part 3: Systems)

14 Jul 2018

This post is the last in a series of three; if you haven’t already read them you might want to go back and read parts 1 and 2 first.

Okay, so now that we’ve tackled “functional” and “object oriented,” what’s wrong with “systems?”

In Part 2, regarding “object oriented” I backed off from my original thesis a bit – the situation wasn’t as bad as I initially perceived. The case with “systems” is much the opposite – at the outset it was clear to me that there was a problem, but it turns out to be much worse than I feared.

Part of the problem with “systems” is there doesn’t seem to be a consensus on the meaning of the term. From what I can tell, there seem to be three main criteria for defining it:

  1. Language features (or lack thereof).
  2. Use cases for the language.
  3. How similar it is to C and C++.

(3) doesn’t seem like a good way of doing things, but we’re probably never going to get away from that kind of thinking.

(1) and (2) might not be so bad if that was the end of the story, but within each of these approaches, nobody seems to agree on much of anything. In the features camp, depending on who you talk to, a language needs to have some subset of the following:

The use case approach has its own grab bag of things the language should be good for:

Neither of these lists is intended to be complete. It’s easy to see that the two lists are related – most of the features are motivated by some subset of the use cases.

If you’ve read my previous posts, by now you’ll have picked up on this pattern of grab-bag lists, and the points I’ve made in the last two posts about their utility as a way of thinking about things all stand up here as well. But it’s worse than that.

Besides the fact that nobody seems to actually agree on which items on the lists are necessary and/or sufficient, a lot of the use cases don’t care about many of the listed features (or lack of), and would be better off without them. For example: many of the uses cases are perfectly compatible with garbage collection, and all of a garbage collector’s usual advantages apply (exceptions: OSes, languages runtimes, maybe drivers).

Let’s look at some of the use cases, compare to the feature lists, and see what the motivation is for various features.

Operating systems

Let’s start with operating systems. In this case a lot of the features are motivated by a need for lack of dependencies; the OS is at the bottom, and things have to start somewhere:

Good performance is on the list because the performance of the OS affects everything and so is critical. A lot of the other features are corollaries of this, even when they have other motivations as well.

Some of them are required for semantics: the problem domain of an operating system involves manipulating machine-level details, hence native machine types, control over memory layout and working with raw bytes.

The ability to make direct system calls doesn’t apply (we are the OS), but it does seem weird that you wouldn’t have it.

It isn’t clear to me that “facilities for programming in the large” fits on the OS list. This is a good thing to have in a language for most any kind of software, but:

So operating systems hits almost every point on the features list. What about some others?

Network servers

Things that absolutely are not required, or even particularly useful for a network server:

These don’t necessarily have anything to do with networking either:

That just leaves:

Which depends on the protocol.

So network servers is on here despite not necessarily needing anything on the features list at all. I think this item found it’s way onto the list via the “like C/C++” approach to the question, and the fact that historically people have used those languages for this.

…Not that they need to these days, and frankly it’s probably a bad idea most of the time. The last thing you need in a language that talks to the internet is the possibility of memory corruption.

Device drivers

Things actually needed (usually):

So about half the list.

Things not needed:

Linkers

Needed:

Needed for dynamic linkers, but not linkers part of an ahead of time compile step:

Note that while performance is always nice, there’s nothing special in this regard about the linker in a development toolchain (as opposed to a dynamic linker).

Compilers

This one’s a doozy: not a single feature on the list is a requirement. Good performance is always nice. You probably need the raw bytes thing for an assembler, but not a compiler. But, frankly, the best-in-breed languages for writing compilers are almost the polar opposite of those great for writing e.g. OSes:

People do write compilers in languages that fit the other categories, but they are much less suited to the task. More often than not, the only benefit for doing so is to have a self-hosting compiler (which has its own trade-offs).


So, lots of variety in requirements. Note that so far nothing has “programming in the large” on its requirements list. Obviously “large codebase” will, but it seems to be entirely orthogonal to the rest of the criteria.

It’s not obvious to me that there’s anything substantive tying any of these ideas together. I think there are two basically coherent ideas that some people mean when they say “systems language”:

  1. Good for writing operating systems.
  2. Like C and C++.

(2) is a dead horse at this point; I’m going to leave it alone. (1) is reasonable. But neither of these definitions have enough consensus behind them for the term to be communicative.