But Zig's comptime only approximates the features you mentioned; it doesn't fully implement them. Which is what the original article is saying. To use your analogy, using a touchscreen to eliminate a keyboard isn't very impressive if your touchscreen keyboard is missing keys.
If you say that incomplete implementations count, then I could argue that the C preprocessor subsumes generics/templates, interfaces/typeclasses†, macros, and conditional compilation.
†Exercise for the reader: build a generics system in the C preprocessor that #error's out if the wrong type is passed using the trick in [1].
> But Zig's comptime only approximates the features you mentioned; it doesn't fully implement them
That's like saying that a touchscreen device without a keyboard only approximates a keyboard but doesn't fully implement one. The important thing is that the feature performs the duty of those other features.
> If you say that incomplete implementations count, then I could argue that the C preprocessor subsumes generics/templates, interfaces/typeclasses†, macros, and conditional compilation.
There are two problems with this, even if we assumed that the power of C's preprocessor is completely equivalent to Zig's comptime:
First, C's preprocessor is a distinct meta-language; one major point of Zig's comptime is that the metalanguage is the same language as the object language.
Second, it's unsurprising that macros -- whether they're more sophisticated or less -- can do the role of all those other features. As I wrote in my original comment (https://news.ycombinator.com/item?id=43745438) one of the exciting things about Zig is that a feature that isn't macros (and is strictly weaker than macros, as it's referentially transparent) can replace them for the most part, while enjoying a greater ease of understanding.
I remember that one of my first impressions of Zig was that it evoked the magic of Lisp (at least that was my gut feeling), but in a completely different way, one that doesn't involve AST manipulation, and doesn't suffer from many of the problems that make List macros problematic (i.e. creating DSLs with their own rules). I'm not saying it may not have other problems, but that is very novel.
I hadn't seen any such fresh designs in well over a decade. Now, it could be that I simply don't know enough languages, but you also haven't named other languages that work on this design principle, so I think my excitement was warranted. I'll let you know if I think that's not only a fresh and exciting design but also a good one in ten years.
BTW, I have no problem with you finding Zig's comptime unappealing to your tastes or even believing it suffers from fundamental issues that may prove problematic in practice (although personally I think that, when considering both pros and cons of this design versus the alternatives, there's some promise here). I just don't understand how you can say that the design isn't novel while not naming one other language with a similar core design: a mechanism for partial evaluation of the object language (with access to additional reflective operations) that replace those other features I mentioned (by performing their duty, if not exactly their mode of operation).
For example, I've looked at Terra, but it makes a distinction between the meta language and the object (or "runtime") language.
> The important thing is that the feature performs the duty of those other features.
Zig's comptime doesn't do everything that Rust (or Java, or C#, or Swift, etc.) generics do, and I know you know this given your background in type theory. Zig doesn't allow for the inference and type-directed method resolution that Rust or the above languages do, because the "generics" that you create using Zig comptime aren't typechecked until they're instantiated. You can improve the error messages using "comptime if" or whatever Zig calls it (at the cost of a lot of ergonomics), but the compiler still can't reliably typecheck the bodies of generic functions before the compiler does the comptime evaluation.
Now I imagine you think that this feature doesn't matter, or at least doesn't matter enough to be worth the complexity it adds to the compiler. (I disagree, of course, because I find reliable IDE autocomplete and inline error messages to be enormously useful when writing generic Rust functions.) But that's the entire point: Zig comptime is not performing the duty of generics; it's approximating generics in a way that offers a tradeoff.
When I first looked at Zig comptime, it didn't evoke the "magic of Lisp" at all in me (and I do share an appreciation of simplicity in programming languages, though I feel like Scheme offers more of that than Lisp). Rather, my reaction was "oh, this is basically just what D does", having played with D a decent amount in years prior. Nothing I've seen in the intervening years has changed that impression. Zig's metaprogramming features are a spin on metaprogramming facilities that D thoroughly explored over a decade before Zig came on the scene.
Edit: Here's an experiment. Start with D and start removing features: GC, the class system, exceptions, etc. etc. Do you get to something that's more or less Zig modulo syntax? From what I can tell, you do. That's what I mean by "not revolutionary".
> Zig doesn't allow for the inference and type-directed method resolution that Rust or the above languages do
Well, but Zig also doesn't allow for overloads and always opts for explicitness regardless of comptime, so I would say that's consonant with the rest of the design.
> Now I imagine you think that this feature doesn't matter, or at least doesn't matter enough to be worth the complexity it adds to the compiler.
I don't care too much about the complexity of the compiler (except in how compilation times are affected), but I do care about the complexity of the language. And yes, there are obviously tradeoffs here, but they're not the same tradeoffs as C++ templates and I think it's refreshing. I can't yet tell how "good" the tradeoff is.
> Here's an experiment. Start with D and start removing features: GC, the class system, exceptions, etc. etc. Do you get to something that's more or less Zig modulo syntax?
For instance, the notion of a comptime variable (for which I couldn't find an analogue in D) is essential to the point that the "metalanguage" and the object language are pretty much the same language.
Interestingly, in Zig, the "metalanguage" is closer to being a superset of the object language whereas in other languages with compile-time phases, the metalanguage, if not distinct, is closer to being a subset. I think Terra is an interesting point of comparison, because there, while distinct, the metalanguage is also very rich.
[1] which, to me, gives the "magical Lisp feeling" except without macros.
I don't think that's the same thing (rather, it's more like ordinary Zig variables in code that's evaluated at compile-time), as there's no arbitrary mixing of compile-time and runtime computation. Again, compare with https://ziglang.org/documentation/master/#Case-Study-print-i...
Anyway, I found this article that concludes that D's compile time evaluation is equivalent in power to Zig's, although it also doesn't cover how comptime variables can be used in Zig: https://renato.athaydes.com/posts/comptime-programming
However, as I've said many times, knowing about the theoretical power of partial evaluation, what excites me in Zig isn't what comptime can do (although I am impressed with the syntactic elegance of the mechanism) but how it is used to avoid adding other features.
A phone with a touchscreen is evolutionary; a phone without a keypad is revolutionary. The revolution is in the unique experience of using "just comptime" for many things.
It is, of course, a tradeoff, and whether or not that tradeoff is "good" remains to be seen, but I think this design is one of the most novel designs in programming languages in many, many years.
>I'm not saying it may not have other problems, but that is very novel.
Just to explicitly acknowledge this, it inherits the C++ problem that you don't get type errors inside a function until you call the function and, when that happens, its not always immediately obvious whether the problem is in the caller or in the callee.
If you say that incomplete implementations count, then I could argue that the C preprocessor subsumes generics/templates, interfaces/typeclasses†, macros, and conditional compilation.
†Exercise for the reader: build a generics system in the C preprocessor that #error's out if the wrong type is passed using the trick in [1].
[1]: https://stackoverflow.com/a/45450646