Why “system level languages” are considered difficult — a different take
Note: This is an article on programming language design.
When we say “system level languages” we tend to think of C, or perhaps C++. Recently we also have Rust trying to fill that role. Still, all of these languages are considered more difficult than writing in say Ruby or Python.
The usual explanation is that C/C++/Rust have manual memory handling and isn’t as object oriented / dynamic as many high-level programming languages. While this is true, I am going to argue that this might not be the real reason.
Consider the task of adding two strings in C:
This task is a bit easier in C++ with STL and in Rust, but fundamentally it’s not a straightforward task. Strings are not primitives in any of the languages.
What about arrays? Adding two arrays requires that you carry around the length of each array, since a C array is simply a pointer to a region of allocated memory.
Surely this is simpler in C++ with STL? Maybe not quite, it’s only a line of code, but it’s certainly not simple (example copied from rosettacode.org)
What about Rust? (Yes there are probably easier ways to do it, the point is just to show that Rust wasn’t written with simple array handling in mind)
Now let’s compare that those examples to Ruby:
For C++ we can — with operator overloading — do something like the Ruby code of course, but that would be missing the point. In Ruby string and arrays act almost like primitives. They’re easy to create and easy to modify. In Rust, C and C++ we need to build every single function by hand, and even then it might feel awkward and difficult. It might not seem much but having to focus on the basic implementation all of the time adds up to quite a bit of work in a larger application.
So low level languages require more work, what else is new… or?
The key insight here is that the actual difficulty in these languages are actually centered around only three constructs: the array, the map and the string. These are things you often have to pass around in high level code, and consequently they tend to be very ergonomic in high level languages. Think about it: isn’t a lot of the work trying to “high level” things in C mostly about needing several lines of code where high level languages with their flexible arrays, maps and strings would only need one?
What if there was a first-class array, map and string in C? You could still do everything you do with pointers today, but when you needed to do something a little less low level you could jump to the built-in arrays, maps and strings that only would have a slight overhead to pure pointer data (certainly a whole lot less than the vector template in C++).
So how would such a “C language with arrays, maps and strings” look like? Interestingly, we can claim that the language exists and is called Go! Here it is interesting to see that Go challenging server side JS, Python, Java etc: all considered “high level languages”.
But wait! — Go has a garbage collector!
While that is true, it’s not the reason why it can replace high level languages. Note how none of the C examples would be different with a GC. I also recommend looking at some non-trivial Go code: the complexity added to Go if it required manual memory management would not necessarily be all that much as long as arrays, maps and strings had a reasonably ergonomic memory model.
PHP is another example. Originally little more than a procedural language with numbers, arrays, maps and strings. Even so it edged out other popular languages. I would claim that in many ways, high level primitives for arrays, maps and string are all that you need in order to make a language “high level”, not OO, not meta programming, not closures or anything similar.
So the next time you wonder how a programming language could straddle that divide between low level performance and high level ergonomics, don’t forget the vital importance of arrays, maps and strings.
This was posted on Reddit and people on r/programming apparently had extreme difficulties understanding the point of this article. It was, of course, a commentary on the many “system level languages” currently in development besides Rust: Zig, Nim, Jai, Crystal, C2 to name a few. The goal is usually greater ergonomics than C but still much of its simplicity.
I’m simply arguing that would-be C replacements should pay attention to how they deal with arrays, maps and strings — not just objects and functions, because that might increase it’s general usability far beyond the domain of system level programming.
A sample of the Reddit comments:
– “This guy is a complete moron […] [A system language] has nothing to do with concatenating strings”
I chose to interpret that as: “always leave a simple introduction so we can understand what the article is about, it’s so easy to misunderstand”