> People in the programming language design community strive to make their languages more expressive, with a strong type system, mainly to increase ergonomics by avoiding code duplication in final software;

I’ve never heard this, thought the reason was to eliminate classes of errors at compile time and to make large-scale refactoring easier, among other things. Or did I just miss this rationale?

Here’s the lede:

> Let us think a little bit about how to workaround the issue. If we make our languages fully dynamic, we will win biformity and inconsistency 13, but will imminently lose the pleasure of compile-time validation and will end up debugging our programs at mid-nights. The misery of dynamic type systems is widely known.

The only way to approach the problem is to make a language whose features are both static and dynamic and not to split the same feature into two parts. Thus, the ideal linguistic abstraction is both static and dynamic; however, it is still a single concept and not two logically similar concepts but with different interfaces 14. A perfect example is CTFE, colloquially known as constexpr: same code can be executed at compile-time under a static context and at run-time under a dynamic context (e.g., when requesting a user input from stdin.); thus, we do not have to write different code for compile-time (statics) and run-time (dynamics), instead we use the same representation.

One possible solution I have seen is dependent types. With dependent types, we can parameterise types not only with other types but with values, too. In a dependently typed language Idris, there is a type called Type – it stands for the “type of all types”, thereby weakening the dichotomy between type-level and value-level. Having such a powerful thing at our disposal, we can express typed abstractions that are usually either built into a language compiler/environment or done via macros. Perhaps the most common and descriptive example is a type-safe printf that calculates types of its arguments on the fly, so let give us the pleasure of mastering it in Idris 15!

…

Overall, the design of Zig’s type system seems reasonable: there is a type of all types called type, and using comptime, we can compute types at compile-time via regular variables, loops, procedures, etc. We can even perform type reflection through the @typeInfo, @typeName, and @TypeOf built-ins! Yes, we can no longer depend on run-time values, but if you do not need a theorem prover, probably full-blown dependent types are a bit of overkill.

…

Everything is good except that Zig is a systems language. On their official website, Zig is described as a “general-purpose programming language”, but I can hardly agree with this statement. Yes, you can write virtually any software in Zig, but should you? My experience in maintaining high-level code in Rust and C99 says NO.

…

Zig can still be used in large systems projects like web browsers, interpreters, and operating system kernels – nobody wants these things to freeze unexpectedly. Zig’s low-level programming features would facilitate convenient operation with memory and hardware devices, while its sane approach to metaprogramming (in the right hands) would cultivate understandable code structure. Bringing it to high-level code would just increase the mental burden without considerable benefits.

…

Final Words

Static languages enforce compile-time checks; this is good. But they suffer from feature biformity and inconsistency – this is bad. Dynamic languages, on the other hand, suffer from these drawbacks to a lesser extent, but they lack compile-time checks. A hypothetical solution should take the best from the both worlds.

Programming languages ought to be rethought.