Programming Languages and their Values

Home · Blog

17 November 2024

I’ve been looking at the Rust, Zig, and Hare programming languages recently, trying to decide which one to use for a hobby project. That comparison has been surprising: the three are very similar in some ways, and very different in others.

When people compare programming languages, they usually come at it from one of two angles, either debating which language is better, or looking for “the right tool for the job.” But neither of these seems applicable here. I would argue that between Rust, Zig, and Hare, none is better than the others -- they were all created by smart people who took the time to come up with a good, coherent design for a programming language. They also have essentially the same goal, which is to provide a more modern alternative to C and C++.

And yet they’re strikingly different. Why is that? It all started making sense to me when I remembered an old podcast interview with Bryan Cantrill where he talks about how different technologies, and the communities around them, have different values. Rust, Zig, and Hare are clearly informed by different values.

I thought it would be fun to see if I can actually come up with a list of values for each, just from looking at the three languages and the differences between them. I added Go as a fourth, partly because it’s what I know best and partly because its values overlap and contrast with the others in interesting ways. In fact, Go is the one I’ll start with.

Go

If you ask Go programmers what they like about it, they’ll often mention its simplicity. The origin of Go is that its creators, who then worked at Google, weren’t happy with Google’s standard options of Java, C++, and Python. Compared to those it is indeed simpler. It’s not minimalist, though. For eample, the built-in map type with its special syntax could have been removed to make the language even simpler.

In Go, simplicity is counter-balanced by another value: pragmatism. The aim of Go was always to make programming more fun and more productive, and that lead to lots of pragmatic choices. The map type is a good example: it’s a hash table type built into the language with its own syntax. From a computer scientist’s perspective that’s an ugly hack (surely it should go in the standard library!), but Go’s creators knew that in day-to-day programming, a good hash table implementation is so useful it’s worth making it an exception.

I’ll add stability as a third value. What I have in mind is Go’s promise of compatibility (see Go 1 and the Future of Go Programs and the update Backward Compatibility, Go 1.21, and Go 2) as well as my own experience switching code from one major version to the next, and the next, and the next, so far without ever having to change code or update any dependencies.

Just for fun, let’s put this into a little chart:

Rust

I started with the most widely-used language on the list, so why not continue with Rust, which seems to be just on the edge of what’s considered a mainstream language today.

My first value for Rust is safety, which won’t surprise you if you’ve had any contact with the language or its community. What I mean by that is more than just memory safety and “no undefined behavior” though. For me, safety means any feature in the language that’s designed to catch mistakes made by its users. Some of Rust’s safety features, in addition to ownership and the borrow checker, include the distinction between mutable and immutable bindings, the rule that match expressions must cover all possible values, and not having null in the language.

A focus on safety could have lead to a verbose, tedious language -- imagine Java 1.0 with lots of extra annotations for ownership and mutability. I think that hasn’t happened because Rust’s creators also value expressiveness, meaning the ability for programmers to express their intent concisely in code. You can see the desire for expressiveness all over the language, from closures and iterators to macros and the derive attribute, but my favorite example is the field init shorthand, which lets you avoid a little bit of repetition when you assign a variable to a field that has the same name as the variable.

For the next value I want to go back to simplicity, to say that Rust’s community doesn’t seem to value simplicity very much, at least compared to the broader programming community. I’m breaking my format a little by putting the absence of a value on the list, but for me those three together are what explains Rust’s design: safety and expressiveness, but not simplicity. Rust is pretty much famous for being difficult to learn, so I don’t think I need to belabor the point here, but just to give a concrete example I’ll point you to Rust’s two different types of macros.

So, here’s my little chart for Rust:

Zig

Zig, like Rust, is a language for writing lower-level, optimized code that helps you avoid the traps of undefined behavior and unsafe memory use. However, it takes a more pragmatic approach to do so: while in Rust the compiler statically checks that your code is safe (unless it’s explicitly marked unsafe), Zig code is unsafe by default but Zig has features that help you write safe code. To name a couple, the defer statement helps to deallocate memory in the right place, and the standard library’s test allocator helps with finding memory leaks. The Zig compiler also has safe and unsafe build modes so you can choose whether safety or speed is more important.

So, as with Go I’ll make pragmatism one of the values. I’ll add simplicity as well. Zig does have a lot of features (just browse the language reference table of contents to get an idea) but most of them are there because they’re needed for the Zig’s stated goals of letting you write optimized code and interfacing with C. For example, it has built-in types for SIMD operations, but that’s something you need for optimized code. Making SIMD support some kind of add-on separate from the base language wouldn’t really simplify things.

My third value for Zig is predictability. What I mean is being able to understand what happens just by reading the code -- in other words, no magic, nothing hidden from the programmer. Zig doesn’t have any hidden control flow (no destructors, for example), no implicit allocations, and no preprocessor that changes the meaning of code. Another example is that identifiers can’t shadow other identifiers to avoid confusion about what an identifier refers to.

Here’s the chart, ⅔ of it the same as for Go:

Hare

Finally: Hare, another modern alternative to C. For Hare, three values stood out very clearly just from reading the documentation and the official blog.

The first one is our old friend simplicity, except here it isn’t tempered by pragmatism. Going back to the example of Go’s map type, Hare doesn’t have an equivalent type in the language or even the standard library -- you’re encouraged to write your own if you need one. Hare’s compiler is kept simple as well, with the intentionally simple QBE compiler backend as its only dependency.

The second is stability. The Hare developers’ aim is that “the language grammar and semantics will be permanently frozen” once Hare 1.0 is released (see Hare aims to become a 100-year programming language). This is a bit theoretical at the moment, as Hare 1.0 hasn’t been released yet, but I assume it is already influencing decisions about the language as well as who decides to join its community.

The third value is freedom, meaning a commitment to free software. Go, Rust, Zig, and Hare are all implemented as free software, but the Hare complier also doesn’t support any proprietary operating system. There’s a Mac OS port maintained by someone else, but the Hare team aren’t providing any support for Mac OS or Windows. This may seem like an extreme position, but if they don’t think proprietary operating systems are a good idea, why should they spend their time supporting them?

Here’s the chart for Hare:

Conclusion

So, was any of this useful in helping me choose a language for my little hobby project? Yes, actually! While figuring out the values for each language, I realized that, at least in the context of programming, I personally really value simplicity, pragmatism, and predictability. So I’ll be going with Zig.