Hacker News new | ask | show | jobs
by chongli 1462 days ago
Complexity and expressivity have a complicated (sorry) relationship. There are tradeoffs all around. On the one hand, a more complicated language allows you to express complicated ideas in a more compact fashion with more help from the compiler to check that what you’re doing is correct.

On the other hand, a complicated language also encourages you to use those complicated features to express yourself in more complicated ways. And this sort of thing, exercised without restraint, can lead to unreadable code. There is a lot to be said for a simple language that encourages you to write simple code and puts an emphasis on readability and maintainability over sheer power. Is Zig that language? I don’t know, but it seems like it’s aiming to be. Only time will tell whether its big statement, that we should prefer comptime code over fancy new language features or powerful macros that let us extend the language in a million ways the way we did with Lisp. I am interested to see how things develop!

3 comments

Another flaw is that a more complicated language and/or one with leaky abstractions (both complex and simple rules) requires spending more mental bandwidth working around the language's pitfalls caused by complex rules. Examples include C++'s template/metaprogramming and linking rules (complex and leaky), heavy use of C macros (obscure and leaky rules, complex in use), C/C++'s header system and ODR (leaky), or Rust trying to mark most pointers as noalias and breaking programs incompatible with that model (complex and leaky) rather than embracing either referential transparency like functional languages or a "pointers are memory addresses" model like assembly/WASM.
“No alias” in Rust is a simple rule that was there from the start (it didn’t break any programs, unless by “programs” you mean “programs I would like to write in a certain way”).
By "breaking programs incompatible with that model" I think he means making it impossible (or requiring a tedious amount of uses of `unsafe`) to write code in Rust to be linked with C code if the C code contains idiom fairly common in C code.
To elaborate, there is a recurring trend of sound C programs turning into unsound Rust programs, because shared mutability is often necessary but Stacked Borrows places strict conditions on constructing &mut T (they invalidate some but not all aliasing *const T), and it's less ergonomic to work solely in raw pointers and avoid creating Box<T> or long-lasting &mut T (or for intrusive collections, any &mut T at all).

For example, matklad (the author of rust-analyzer, one of the preeminent Rust programmers and someone I'd expect to get code right) made a recent blog post on "Caches In Rust" (https://matklad.github.io/2022/06/11/caches-in-rust.html). The cache is built around https://docs.rs/elsa, which is built around https://docs.rs/stable_deref_trait/latest/stable_deref_trait..., which is unsound for Box and violates stacked borrows in its current form (https://github.com/Storyyeller/stable_deref_trait/issues/15). However, the rules may be relaxed or more ergonomic alternatives added (https://github.com/rust-lang/unsafe-code-guidelines/issues/3...), it's uncertain right now.

(Also I go by "they".)

I'd say power of expression is more closely correlated to the ability to make the code complicated. Making a large language is onoy one way to get that power.

Zig's metaprogramming is not far off being as powerful as lisp. Of rust and zig, I'd expect zig code to be more capable of turning into a hot mess more quickly.

Hopefully its simplicity and the niche it is targetting will help the community keep it somewhat in check.

How funny that you write this comment as if “comptime” is the thing that is the “simple code” alternative in this context: most static languages go with the usual angle-brackets thing for generics, which is fairly declarative and somewhat limited. Zig however has gone with a more general approach: use “comptime” functions which take types as arguments in order to specialize functions. That is way more fancy.

You can get away with not writing macros in most languages that have them. But a lot of static languages makes you use generics (parametric polymorphism) pretty frequently. And this two-stage evaluation (something like `funny(@comptime T: Type, a: T, …)`) is the simple-code flagship feature, in your book?

> most static languages go with the usual angle-brackets thing for generics, which is fairly declarative and somewhat limited. Zig however has gone with a more general approach: use “comptime” functions which take types as arguments in order to specialize functions. That is way more fancy.

This seems like you're comparing the most basic usage of C++ templates to the full generality of Zig comptime. But isn't this ignoring the fact that people write absurdly complicated template metaprograms in C++ that are very difficult to understand and debug? C, C++, and Rust require you to learn a second language to do metaprogramming, Zig doesn't.

I was comparing generics in Java and Rust with generics in Zig.