Here’s a non-exhaustive rundown of newish systems languages. I’ll list some notable things about them related to safety and syntax as I discussed in the previous post. Well, here they are, in rough order of production readiness and popularity. Sorry if I put something lower than it deserved.
While putting this list together I wondered why so many languages have gotten built recently. In part it’s that tools like llvm are just more powerful than the older compiler construction tools, but the bar for a new language has gotten higher too. Could it be the existence of LLVM ? I’m not sure – a number of these languages use C or C++ or QBE as back ends instead. I make a note on the back end for each as a point of interest.
1. Rust Rust home page
- Sum types via enum and pattern matching
- Expressions only, no statements
- Borrow checker to enforce single ownership and single borrower of mutable data
- Manual memory management (but with lifetimes and borrow checking, no manual alloc or free is needed.)
- Good built-in threading support including memory safety for concurrent programs
- LLVM backend, alternative gcc, Crane-lift
I shouldn’t need to say too much about Rust, it’s the big player and is the only one on this list past a 1.0 release.
The ability to have non-reference counted, non-GC (so sort of manual) memory management while keeping Rust programs very memory safe and fast is the big claim to fame of Rust. But combining that with other good code safety like sum types and pattern matching makes Rust great. Safe Rust, (there can be blocks marked “unsafe” for exceptional situations,) offers a lot of practical safety. Rust is one of those languages where if you can get your code to compile there’s a good chance it won’t crash and will actually do what you intended on the first try.
The thing I dislike the most about Rust is the fact that lifetimes are so often automatically elided and invisible; so much so that when you actually must set them explicitly it’s a struggle at times because it happens so rarely and you’re out of practice. Also the use of traits can get out of hand in my opinion.
Oh, and lastly the borrow checker makes it hard to define some data structures that are trivial with a garbage-collected language. I tend to use recursive patterns learned from Scheme, Ruby and Java which don’t fit well with Rust. With the right lifetimes you can do it though. Sometimes you are better off reworking your code to solve the problem without these graph-like data structures.
2. Zig Zig home page
- Only manual memory management but more streamlined than C and allows simple RIAA patterns with “defer”
- Supports tagged unions and exhaustive switch statements
- Compile time function execution
- Predictability as a language feature: No hidden control flow, no hidden allocations, no macros or meta-programming
- Can mix Zig and C and C++ compilation units
- LLVM back end
While Zig isn’t at a 1.0 release yet it’s quite polished for a 0.10.1 version. It may be the most C-like in spirit of any language here, and produces some of the fastest programs of any language. Zig is probably the most popular and successful new systems language next to Rust.
Like other new languages it is substantially more safe than C even though it has fewer safety innovations than most other languages in this list. While all memory allocation is manual, you can easily change allocators and choose a debugger allocator to check for memory safety. In fact the fact you must choose an allocator is a feature: It’s easy to tell by reading Zig code if any allocations are done or not. To allocate you must have an allocator.
Zig is very impressive for providing an alternative C and C++ tool chain. If it weren’t for some highly restrictive conditions on our build environment, I’d re-do my current large C++ project at work to build with Zig.
The documentation is somewhat behind Rust but the project is at an earlier development stage, so that’s to be expected. There are lots of good example code snippets on the home page showing language features in use and a good language reference.
3. Odin Odin home page
- Manual memory management including defer and easy to use allocators
- Simple syntax: No overloading of operators, no uniform function call syntax
- “Distinct” types: Actual new types based on existing types for stronger type checking.
- Tagged unions
- LLVM backend
Odin is designed for simplicity of syntax, high performance compilation and high performance executables. It is influenced by Pascal, Oberon and other Wirth languages which is a good thing. I really like many of the design choices in Odin. While it may have the fewest safety affordances compared to other modern systems languages, it still provides much better safety than C++ at little expense.
Odin has imperative statements like C, Pascal etc rather than Rust or Ruby’s”everything’s an expression” approach. Odin compensates well by offering some nice operators like “or_else” to allow conditional expressions. Once you have gotten used to “if” as an expression it’s hard to go back.
Odin is being used for real high-performance applications. It has a lot of support for inter-operating with external libraries and has bindings for popular graphics and audio libraries.
As a game-dev adjacent language it kind of makes sense that complex numbers and quaternions are built-in types in the language.
4. Jakt Jakt home page
- Memory safe with reference counting
- Sum types with enums and pattern matching
- Value semantics with struct, reference semantics with classes
- Set types
- Superficially reference syntax is like Rust, but without the borrow-checker applied to them
- Compile time function execution
- C++ output – c++ compiler as the backend
Jakt is a part of the Serenity OS project. Jakt is meant to be the language for writing Serenity applications. It was created in 2022 very quickly and is already self-hosting. Jakt isn’t limited to use in Serenity however; it currently generates C++ which could be compiled almost anywhere. The pace of development on this language is really impressive.
Interestingly One of Jakt’s top goals was readability. For instance function arguments are always named, not positional as in most languages.
The designers made many choices that really agree with my own view of how a programming language should be.
5. Hare Home
- Manual memory management
- Tagged unions*
- Bounds checking
- mandatory initializers
- Mandatory error handling or immediate termination
- Exhaustive switch and match
- Nullable pointers
- QBE backend
Hare is very new, the first open source release was announced in 2022. Hare comes closest to a direct C replacement. It’s small: (fits on a 3.5” floppy disk.) It uses QBE as the compiler back end which partly accounts for it’s compactness. QBE is an optimizing back end written in C that’s much smaller than LLVM.
Hare has various features to improve on C’s spatial memory safety, but not so much temporal safety. The road map indicates they’re considering a borrow-checker though. The exhaustive match and switch along with forced error handling and forced handling of null values make it a lot less bug-prone than C in general. Statements are expressions, so as in Rust you can initialize a variable with the result of an “if” expression-statement, for example.
The Hare developers plan to only support free platforms, so they won’t ever support Mac OS or Windows.
There’s a document on the Hare home page dedicated to Hare safety features.
6. Vale Vale home page
- Automatic, very fast memory management
- Higher Raii: What’s this?
- Single ownership without the need for a borrow-checker
- LLVM backend
Vale has “fast, safe, easy” as a goal. Vale uses a novel technique to manage its memory called generational references. It’s a sort of reference counting technique but with ownership analysis so that actual reference counts and free / allocation can be kept to a minimum. They claim Vale is “the most safe” natively compiled language. A Region Borrow Checker is under development to make the language even faster and safer.
The language looks very promising already. There are many features beyond what I’ve listed here that are aspirational: The language is early alpha presently. The memory management and single ownership story is very compelling.
7. Lobster Home Page
- Memory management: “compile-time reference counting”
- Statically typed, with a lot of type inference
- “Zero -cost” structs on the stack
- C++ back end
In terms of style Lobster feels like the child of Python and Crystal (which looks like typed Ruby.) It uses indentation for scoping and allows blocks passed as the last argument like in Ruby and Crystal.
Unlike Python and Ruby there’s no garbage collector, and Lobster is compiled. Instead it manages memory with a reference counting approach, but better, with something the author calls “compile-time reference counting.” Here’s a write-up.
Lobster focuses on game and graphics application development though it can be used for anything. There’s a great tutorial that uses a simple game to teach the language. As a language it’s less experimental than others with the exception of the memory management system. While the language syntax doesn’t break new ground it nevertheless looks like a lot of fun as aplatform for small game projects.
I have to commend the author on the documentation quality. It may not be perfect but it’s highly informatively and easy to understand. There’s a nice language reference, a “C programmer’s Cheat Sheet”, quality build instructions, tutorials and background information on memory management and more.
8. Austral Austral home page
This language is at an early stage of development. While the whole language has been implemented, the standard library is still under construction. The build system is at an MVP level currently. Nevertheless it’s extremely interesting. It’s unique in Austral’s careful selection of a few key features: Linear types and type classes most notably.
- Linear types
- Memory management: Automatic with linear types, no GC
- Type classes
- Capability based security
- Sum types with unions
- Exhaustive case statement
- C backend for now
Austral sort of looks like a mini-Ada at first glance. Superficially the syntax is similar. This hints at it’s intended use. Austral is designed to produce very safe programs with fairly easy to read syntax. There’s a big list of anti-features – Austral keeps things simple.
The most interesting features are linear types and type classes. Linear types partly fill the role of Rust’s borrow-checker but offer even more safety due to their strictness. Austral uses a borrow checker but it is simpler than Rust’s; linear types use lexical scope. What are linear types? Rust uses Affine types which are more permissive than linear types and perhaps surprisingly a little harder to reason about and check. Linear types can take care of not just memory safety but resource safety (open files, network connections) generally.
Austral provides polymorphism in the form of type classes. You can think of type classes very approximately as overloaded functions, but done in a way that’s easier to disambiguate and type-check with useful restrictions.
It’s interesting to note the current (bootstrapping) Austral compiler is written with OCaml.
9. Myrddan Myrddan home page
- Algebraic Data Types
- Pattern matching
Myrddan fits squarely in the middle of these other systems languages. It’s a bit older than some others. I’m not sure it’s heavily maintained at this point though it’s usable.
10. V V home page
I don’t know as much about V as some of these other languages. The home page claims:
- GC memory management – kept to a minimum for performance
- Simple syntax, similar to Go
- Limited pattern matching
- Sum types
- Error handling with Result sum type
- C back end
It appears to borrow some good parts from Rust and Go, with a somewhat more sophisticated type system than Go has, but keeping the garbage collector and simple concurrency support. To me these feel like good trade offs. It has built-in JSON support and appears to have some other “batteries included” libraries under development. They already have a package manager. I can’t speak to the quality of the current release (it’s version 0.3.3. Last year there was some online controversy a over how much of the claimed functionality actually worked.
There’s a C to V converter. Like Jakt or Nim you could probably include C source pretty easily and use external libraries with V.
These are two more I just stumbled on. I haven’t had time to research them much but thought they were interesting.
Two Bonus Languages
Cone This looks really cool. It’s not clear to me how much of the features I read about are implemented – they make it clear the list is aspirational (there’s a ‘plan.md’ but not detailed.) On the other hand they have IDE support and a language web playground already and the whole thing looks pretty polished. Cone home page
Compis This is a neat concept. You can intermix Compis source files with C. It compiles to C or WASM and you can use to build C projects. The language looks something like Rust but has no borrow checker. It does track memory with ownership. Overall looks nice. Compis home page.
The Older Generation
Until recently, if we wanted to escape C or C++ to do systems development we had these slightly older languages, all usable for production. They all use garbage collectors, with some escape hatches. They aren’t quite comparable with the new systems languages covered but were the closest thing that could be considered as near – if not in – the mainstream.
I’m leaving out the JVM and Microsoft .NET languages since they require a heavy runtime VM and don’t fill the same niche.
Nim : GC or ARC, compiles to C or WASM. Sort of a Modula-2 with Python-like whitespace scoping. Nim home.
Crystal: GC. Ruby-like syntax, extremely powerful type-inference, union types. Crystal rocks. Crystal home
Go : GC, compiler makes self-contained binaries, good concurrency support in standard library. Backed by Google / Alphabet. Go home.
D : GC or manual, has “better C” support; pure functions, extremely fast compiler, multi-paradigm. Lots of nice little features. D is a multi-paradigm language. It’s a lot like a “better C++”, but more than just that. D home
Finally, you could use Ada. It’s not new at all, but it possesses most of the qualities of the new systems programming languages I featured already and it’s not C++ or C!
The OG Safe Systems Language: Ada AdaCore home page
Ada is old (1980), not new at all!
- An even safer “Spark” subset which can prove the absence of runtime errors and eliminate most sources of incorrectness
- Manual memory management. Most Ada programs won’t need heap allocation much; you can do most things on the stack. Also, there’s no “delete” keyword – literally none. Only
new. So it’s pretty safe on that score. You can re-use memory if you really needed to. The language design is such that this isn’t the huge missing feature it might appear.
- Extremely efficient code generated by the compiler, as fast or faster than C++
- Immutable function parameters by default
- Highly descriptive type system, variant types, contracts with pre and post conditions, non-nullable types
- Language syntax designed to make writing bugs with typos very unlikely
- Concurrency as part of the language
Read more about Ada on a ‘Random Walk Through Ada’.