I have often heard Zig to be the ‘successor to C’, or even ‘a better C’. While that might or might not be the case, Zig might actually (also) be the ‘successor’ to another system programming language, Ada.
Ada is a programming language from the 80s for embedded, real-time safety critical systems. It was developed partially by the US military. While new projects rarely use it today, it is still widely used in existing systems in the defence industry and in aviation.
Most people I’ve talked to that have heard of Ada, usually think of SPARK, which is a formal verification system (a language extension in a sense) for Ada. SPARK is rarely used, as it hasn’t been available that long. Normal Ada is still a considerably safe language.
Which makes the comparison to Zig interesting, because feature-wise Ada was ahead of its time.
Ignoring petty syntax comparisons, the languages are quite comparable to an extent:
- Ada enforces a large number of checks at compile time, to catch common mistakes. Extremely strict typing prevents a lot of possible bugs. An example are very range violations on arrays. There is also a (payed) static analysis tool, which catches a lot of potential bugs, like use after free. The Zig compiler can currently do most of this analysis (for free, as I might add).
- While pointer usage is limited, untyped pointers never exist. Usually one can rely on the compiler to make smart decisions. Most stuff is just passed as slices under the hood.
- Generics are comparable. Ada has package (namespace) wide compile-time generics. A lot of typ strictness is enforced, but if you think of packages as structs (as they are in Zig), it is functionally the same thing.
- The natural way to write Ada code is to write data oriented code. While OOP exists to some degree, its ergonomics are terrible. Therefor, it’s usually only good for Object dot Function syntax and a form of scope based resource handling (constructor, destructor, but weird). The heavy lifting is usually done with variant records (packed unions).
- It provides a container library in the std. This isn’t special but it prevents most of the use cases for manual memory management.
- A lot of heavy lifting is done through enums. Interestingly, the enum data structures in the zig std all have equivalents in Ada, but not as containers, but as array like types.
- It supports a form of region based memory management. It’s not as powerful as zig allocators, but you’re still able to write slab and arena like ‘pools’.
While the languages do not look the same, it feels surprisingly simular to write Ada and Zig.
However, why post this here? If I haven’t entertained you so far, I think Zig can also take some lessons from Ada. There are also a few features from Ada, I’d like to see in Zig.
- It is generally a very good idea, to have a high quality large std, in favor of a package manager. Rust programs have reached npm levels of dependencies and I blame the comfort of finding and adding packages. Since Ada never was a widely adopted language, outside of specific industries, there are few to no open source libraries one could use. This forced people to write their own libraries. As a result, all Ada projects I’ve seen had few to no dependencies. A ‘boost’ like library doesn’t exist. I hope that Zig never adds a package manager or a public zig repository. After Zig 1.0, I would really like the std to grow, as e.g. a lot of useful but less commonly used data structures are currently missing.
- Do not fall into the trap of overstrict typing. I’ve seen a proposal for a very complicated typedef, which looked very simular to adas types and subtypes (which have very complicated coercion rules). It is not a good idea; People lack the discipline to use the correctly. Either it is distinct, or it isn’t, and these use cases are covered by enums. Range types however sound like a great ideas; They make handles distinct from ‘real integers’ in cases where you can’t use slices.
- I would really like real time capabilities for Zig, and I think it’s headed the right way. Ada has the Ravenscar profiler, hard real time concurrent systems, which makes code safe and predictable. To my knowledge, it is the only compiled language to have such a standard as part of its language and compiler specification. In Zig, I can easily imagine a real time IO interface and a real time allocator. If you think about it, the Zig compiler could also have simular capabilities to Ravenscar. This would be about proving, that code is deterministic. As async/allocations are ‘just code’, you could have purely semantics based analysis. A lot of hard real time coding patterns are already common or baked into Zig if you think about it. E.g. it is common to give upper limits to while loops. Zig also prevents some anti real time patterns all together; Dispatch is static by default, or implemented through tagged unions. (Is Unbounded recursion detected by the zig compiler btw?)
I hope this was decently interesting to read.