Most active commenters
  • WalterBright(14)
  • pron(11)
  • pcwalton(9)
  • matklad(6)
  • edflsafoiewq(4)
  • msteffen(3)
  • naasking(3)

←back to thread

Things Zig comptime won't do

(matklad.github.io)
458 points JadedBlueEyes | 59 comments | | HN request time: 1.993s | source | bottom
Show context
pron ◴[] No.43745438[source]
Yes!

To me, the uniqueness of Zig's comptime is a combination of two things:

1. comtpime replaces many other features that would be specialised in other languages with or without rich compile-time (or runtime) metaprogramming, and

2. comptime is referentially transparent [1], that makes it strictly "weaker" than AST macros, but simpler to understand; what's surprising is just how capable you can be with a comptime mechanism with access to introspection yet without the referentially opaque power of macros.

These two give Zig a unique combination of simplicity and power. We're used to seeing things like that in Scheme and other Lisps, but the approach in Zig is very different. The outcome isn't as general as in Lisp, but it's powerful enough while keeping code easier to understand.

You can like it or not, but it is very interesting and very novel (the novelty isn't in the feature itself, but in the place it has in the language). Languages with a novel design and approach that you can learn in a couple of days are quite rare.

[1]: In short, this means that you get no access to names or expressions, only the values they yield.

replies(7): >>43745704 #>>43745928 #>>43746682 #>>43747113 #>>43747250 #>>43749014 #>>43749546 #
1. WalterBright ◴[] No.43747250[source]
It's not novel. D pioneered compile time function execution (CTFE) back around 2007. The idea has since been adopted in many other languages, like C++.

One thing it is used for is generating string literals, which then can be fed to the compiler. This takes the place of macros.

CTFE is one of D's most popular and loved features.

replies(5): >>43747836 #>>43747875 #>>43749766 #>>43750357 #>>43751134 #
2. az09mugen ◴[] No.43747836[source]
A little bit out of context, I just want to thank you and all the contributors for the D programming language.
replies(1): >>43749238 #
3. msteffen ◴[] No.43747875[source]
If I understand TFA correctly, the author claims that D’s approach is actually different: https://matklad.github.io/2025/04/19/things-zig-comptime-won...

“In contrast, there’s absolutely no facility for dynamic source code generation in Zig. You just can’t do that, the feature isn’t! [sic]

Zig has a completely different feature, partial evaluation/specialization, which, none the less, is enough to cover most of use-cases for dynamic code generation.”

replies(4): >>43748490 #>>43749693 #>>43750330 #>>43755195 #
4. WalterBright ◴[] No.43748490[source]
The partial evaluation/specialization is accomplished in D using a template. The example from the link:

    fn f(comptime x: u32, y: u32) u32 {
        if (x == 0) return y + 1;
        if (x == 1) return y * 2;
        return y;
    }
and in D:

    uint f(uint x)(uint y) {
        if (x == 0) return y + 1;
        if (x == 1) return y * 2;
        return y;
    }
The two parameter lists make it a function template, the first set of parameters are the template parameters, which are compile time. The second set are the runtime parameters. The compile time parameters can also be types, and aliased symbols.
replies(2): >>43752564 #>>43753736 #
5. WalterBright ◴[] No.43749238[source]
That means a lot to us. Thanks!
6. baazaa ◴[] No.43749693[source]
that's a comically archaic way of using the verb 'to be', not a grammatical error. you see it in phrases like "to be or not to be", or "i think, therefore i am". "the feature isn't" just means it doesn't exist.
replies(1): >>43755258 #
7. Someone ◴[] No.43749766[source]
> D pioneered compile time function execution (CTFE) back around 2007

Pioneered? Forth had that in the 1970s, lisp somewhere in the 1960s (I’m not sure whether the first versions of either had it, so I won’t say 1970 respectively 1960), and there may be other or even older examples.

replies(1): >>43754216 #
8. sixthDot ◴[] No.43750330[source]
Sure, CTFE can be used to generate strings, then later "mixed-in" as source code, but also can be used to execute normal functions and then the result can be stored in a compile-time constant (in D that's the `enum` storage class), for example generating an array using a function literal called at compile-time:

   enum arr = { return iota(5).map!(i => i * 10).array; }();
   static assert(arr == [0,10,20,30,40]);
9. throwawaymaths ◴[] No.43750357[source]
You're missing the point. If anything D is littered with features and feature bloat (CTFE included). Zig (as the author of the blog mentions) is more than somewhat defined by what it can't do.
replies(1): >>43754253 #
10. pron ◴[] No.43751134[source]
It is novel to the point of being revolutionary. As I wrote in my comment, "the novelty isn't in the feature itself, but in the place it has in the language". It's one thing to come up with a feature. It's a whole other thing to position it within the language. Various compile-time evaluations are not even remotely positioned in D, Nim, or C++ as they are in Zig. The point of Zig's comptime is not that it allows you to do certain computations at compile-time, but that it replaces more specialised features such as templates/generics, interfaces, macros, and conditional compilation. That creates a completely novel simplicity/power balance.

If the presence of features is how we judge design, then the product with the most features would be considered the best design. Of course, often the opposite is the case. The absence of features is just as crucial for design as their presence. It's like saying that a device with a touchscreen and a physical keyboard has essentially the same properties as a device with only a touchscreen.

If a language has a mechanism that can do exactly what Zig's comptime does but it also has generics or templates, macros, and/or conditional compilation, then it doesn't have anything resembling Zig's comptime.

replies(1): >>43753970 #
11. msteffen ◴[] No.43752564{3}[source]
Here is, I think, an interesting example of the kind of thing TFA is talking about. In case you’re not already familiar, there’s an issue that game devs sometimes struggle with, where, in C/C++, an array of structs (AoS) has a nice syntactic representation in the language and is easy to work with/avoid leaks, but a struct of arrays (SoA) has a more compact layout in memory and better performance.

Zig has a library to that allows you to have an AoS that is laid out in memory like a SoA: https://zig.news/kristoff/struct-of-arrays-soa-in-zig-easy-i... . If you read the implementation (https://github.com/ziglang/zig/blob/master/lib/std/multi_arr...) the SoA is an elaborately specialized type, parameterized on a struct type that it introspects at compile time.

It’s neat because one might reach for macros for this sort of the thing (and I’d expect the implementation to be quite complex, if it’s even possible) but the details of Zig’s comptime—you can inspect the fields of the type parameter struct, and the SoA can be highly flexible about its own fields—mean that you don’t need a macro system, and the Zig implementation is actually simpler than a macro approach probably would be.

replies(1): >>43753986 #
12. naasking ◴[] No.43753736{3}[source]
Using a different type vs. a different syntax can be an important usability consideration, particularly since D also has templates and other features, where Zig provides only the comptime type for all of them. Homogeneity can also be a nice usability win, though there are downsides as well.
replies(1): >>43754154 #
13. WalterBright ◴[] No.43753970[source]
> Various compile-time evaluations are not even remotely positioned in D, Nim, or C++ as they are in Zig.

See my other reply. I don't understand your comment.

https://news.ycombinator.com/item?id=43748490

replies(1): >>43755065 #
14. WalterBright ◴[] No.43753986{4}[source]
D doesn't have a macro system, either, so I don't understand what you mean.
replies(1): >>43756684 #
15. WalterBright ◴[] No.43754154{4}[source]
Zig's use of comptime in a function argument makes it a template :-/

I bet if you use such a function with different comptime arguments, compile it, and dump the assembler you'll see that function appearing multiple times, each with somewhat different code generated for it.

replies(1): >>43754242 #
16. WalterBright ◴[] No.43754216[source]
True, but consider that Forth and Lisp started out as interpreted languages, meaning the whole thing can be done at compile time. I haven't seen this feature before in a language that was designed to be compiled to machine code, such as C, Pascal, Fortran, etc.

BTW, D's ImportC C compiler does CTFE, too!! CTFE is a natural fit for C, and works like a champ. Standard C should embrace it.

replies(1): >>43754779 #
17. naasking ◴[] No.43754242{5}[source]
> Zig's use of comptime in a function argument makes it a template :-/

That you can draw an isomorphism between two things does not mean they are ergonomically identical.

replies(1): >>43755013 #
18. WalterBright ◴[] No.43754253[source]
I fully agree that the difference is a matter of taste.

All living languages accrete features over time. D started out as a much more modest language. It originally eschewed templates and operator overloading, for example.

Some features were abandoned, too, like complex numbers and the "bit" data type.

19. Someone ◴[] No.43754779{3}[source]
Nitpick: Lisp didn’t start out as an interpreted language. It started as an idea from a theoretical computer scientist, and wasn’t supposed to be implemented. https://en.wikipedia.org/wiki/Lisp_(programming_language)#Hi...:

"Steve Russell said, look, why don't I program this eval ... and I said to him, ho, ho, you're confusing theory with practice, this eval is intended for reading, not for computing. But he went ahead and did it. That is, he compiled the eval in my paper into IBM 704 machine code, fixing bugs, and then advertised this as a Lisp interpreter, which it certainly was. So at that point Lisp had essentially the form that it has today”

20. pcwalton ◴[] No.43755013{6}[source]
When we're responding to quite valid points about other languages having essentially the same features as Zig with subjective claims about ergonomics, the idea that Zig comptime is "revolutionary" is looking awfully flimsy. I agree with Walter: Zig isn't doing anything novel. Picking some features while leaving others out is something that every language does; if doing that is enough to make a language "revolutionary", then every language is revolutionary. The reality is a lot simpler and more boring: for Zig enthusiasts, the set of features that Zig has appeals to them. Just like enthusiasts of every programming language.
replies(4): >>43755143 #>>43755308 #>>43757580 #>>43761282 #
21. pron ◴[] No.43755065{3}[source]
The revolution in Zig isn't in what the comptime mechanism is able to do, but how it allows the language to not have other features, which is what gives that language its power to simplicity ratio.

Let me put it like this: Zig's comptime is a general compilation time computation mechanism that has introspection capabilities and replaces generics/templates, interfaces/typeclasses, macros, and conditional compilation.

It's like that the main design feature of some devices is that they have a touchscreen but not a keyboard. The novelty isn't the touchscreen; it's in the touchscreen eliminating the keyboard. The touchscreen itself doesn't have to be novel; the novelty is how it's used to eliminate the keyboard. If your device has a touchscreen and a keyboard, then it does not have the same design feature.

Zig's novel comptime is a mechanism that eliminates other specialised features, and if these features are still present, then your language doesn't have Zig's comptime. It has a touchscreen and a keyboard, whereas Zig's novelty is a touchscreen without a keyboard.

replies(1): >>43755336 #
22. pron ◴[] No.43755143{7}[source]
I'm sorry, but not being able to see that a design that uses a touchscreen to eliminate the keyboard is novel despite the touchscreen itself having been used elsewhere alongside a keyboard, shows a misunderstanding of what design is.

Show me the language that used a general purpose compile-time mechanisms to avoid specialised features such as generics/templates, interfaces/typeclasses, macros, and conditional compilation before Zig, then I'll say that language was revolutionary.

I also find it hard to believe that you can't see how replacing all these features with a single one (that isn't AST macros) is novel. I'm not saying you have to think it's a good idea -- that's a matter of personal taste (at least until we can collect more objective data) -- but it's clearly novel.

I don't know all the languages in the world and it's possible there was a language that did that before Zig, but none of the languages mentioned here did. Of course, it's possible that no other language did that because it's stupid, but that doesn't mean it's not novel (especially as the outcome does not appear stupid on the face of it).

replies(1): >>43755588 #
23. CRConrad ◴[] No.43755195[source]
> the feature isn’t! [sic]

To be, or not to be... The feature is not.

(IOW, English may not be the author's native language. I'm fairly sure it means "The feature doesn't exist".)

24. CRConrad ◴[] No.43755258{3}[source]
Damn, beat me by half a day.
25. matklad ◴[] No.43755308{7}[source]
>for Zig enthusiasts, the set of features that Zig has appeals to them. Just like enthusiasts of every programming language.

I find it rather amusing that it's a Java and a Rust enthusiast who are extolling Zig approach here! I am not particularly well read with respect to programming languages, but I don't recall many languages which define generic pair as

    fn Pair(A: type, B: type) type {
        return struct { fst: A, snd: B };
    }
The only one that comes to mind is 1ML, and I'd argue that it is also revolutionary.
replies(2): >>43755480 #>>43755521 #
26. WalterBright ◴[] No.43755336{4}[source]
The example of a comptime parameter to a function is a template, whether you call it that or not :-/ A function template is a function with compile time parameters.

The irony here is back in the 2000's, many programmers were put off by C++ templates, and found them to be confusing. Myself included. But when I (belatedly) realized that function templates were functions with compile time parameters, I had an epiphany:

Don't call them templates! Call them functions with compile time parameters. The people who were confused by templates understood that immediately. Then later, after realizing that they had been using templates all along, became comfortable with templates.

BTW, I wholeheartedly agree that it is better to have a small set of features that can do the same thing as a larger set of features. But I'm not seeing how comptime is accomplishing that.

replies(1): >>43755439 #
27. pron ◴[] No.43755439{5}[source]
> But I'm not seeing how comptime is accomplishing that.

Because Zig does the work of C++'s templates, macros, conditional compilation, constexprs, and concepts with one relatively simple feature.

replies(1): >>43755658 #
28. pcwalton ◴[] No.43755480{8}[source]
Well, if you strip away the curly braces and return statement, that's just a regular type definition. Modeling generic types as functions from types to types is just System F, which goes back to 1975. Turing-complete type-level programming is common in tons of languages, from TypeScript to Scala to Haskell.

I think the innovation here is imperative type-level programming--languages that support type-level programming are typically functional languages, or functional languages at the type level. Certainly interesting, but not revolutionary IMO.

replies(1): >>43755656 #
29. WalterBright ◴[] No.43755521{8}[source]
I might be misunderstanding something, but this is how it works in D:

    struct Pair(A, B) { A fst; B snd; }

    Pair!(int, float) p; // declaration of p as instance of Pair
It's just a struct with the addition of type parameters.
30. pcwalton ◴[] No.43755588{8}[source]
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].

[1]: https://stackoverflow.com/a/45450646

replies(1): >>43756165 #
31. matklad ◴[] No.43755656{9}[source]
The thing is, this is not type-level programming, this is term-level programming. That there's no separate language of types is the feature. Functional/imperative is orthogonal. You can imagine functional Zig which writes

    Pair :: type -> type -> type
    let Pair a b = product a b 
This is one half of the innovation, dependent-types lite.

The second half is how every other major feature is expressed _directly_ via comptime/partial evaluation, not even syntax sugar is necessary. Generic, macros, and conditional compilation are the three big ones.

replies(1): >>43755774 #
32. WalterBright ◴[] No.43755658{6}[source]
From the article:

    fn print(comptime T: type, value: T) void {
That's a template. In D it looks like:

    void print(T)(T value) {
which is also a template.
replies(1): >>43755843 #
33. pcwalton ◴[] No.43755774{10}[source]
> This is one half of the innovation, dependent-types lite.

But that's not dependent types. Dependent types are types that depend on values. If all the arguments to a function are either types or values, then you don't have dependent types: you have kind polymorphism, as implemented for example in GHC extensions [1].

> The second half is how every other major feature is expressed _directly_ via comptime/partial evaluation, not even syntax sugar is necessary. Generic, macros, and conditional compilation are the three big ones.

I'd argue that not having syntactic sugar is pretty minor, but reasonable people can differ I suppose.

[1]: https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/poly...

replies(1): >>43756112 #
34. pcwalton ◴[] No.43755843{7}[source]
I think another way to put it is that the fact that Zig reuses the keyword "comptime" to denote type-level parameters and to denote compile-time evaluation doesn't mean that there's only one feature. There are still two features (templates and CTFE), just two features that happen to use the same keyword.
replies(2): >>43756052 #>>43756175 #
35. pron ◴[] No.43756052{8}[source]
Maybe you can insist that these are two features (although I disagree), but calling one of them templates really misses the mark. That's because, at least in C++, templates have their own template-level language (of "metafunctions"), whereas that's not the case in Zig. E.g. that C++'s `std::enable_if` is just the regular `if` in Zig makes all the difference (and also shows why there may not really be two features here, only one).
replies(3): >>43756271 #>>43756316 #>>43760879 #
36. matklad ◴[] No.43756112{11}[source]
> Dependent types are types that depend on values.

Like this?

    fn f(comptime x: bool) if (x) u32 else bool {
        return if (x) 0 else false;
    }
replies(2): >>43756294 #>>43756555 #
37. pron ◴[] No.43756165{9}[source]
> 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.

replies(2): >>43756323 #>>43758823 #
38. edflsafoiewq ◴[] No.43756175{8}[source]
They are the same thing though. Conceptually there's a partial evaluation pass whose goal is to eliminate all the comptimes by lowering them to regular runtime values. The apparent different "features" just arise from its operation on the different kinds of program constructs. To eliminate a expression, it evaluates the expression and replaces it with its value. To eliminate a loop, it unrolls it. To eliminate a call to a function with comptime arguments, it generates a specialized function for those arguments and replaces it with a call to the specialized function.
39. sekao ◴[] No.43756271{9}[source]
Agreed. Zig's approach re-uses the existing machinery of the language far more than C++ templates do. Another example of this is that Zig has almost no restrictions on what kinds of values can be `comptime` parameters. In C++, "non-type template parameters" are restricted to a small subset of types (integers, enums, and a few others). Rust's "const generics" are even more restrictive: only integers for now.

In Zig I can pass an entire struct instance full of config values as a single comptime parameter and thread it anywhere in my program. The big difference here is that when you treat compile-time programming as a "special" thing that is supported completely differently in the language, you need to add these features in a painfully piecemeal way. Whereas if it's just re-using the machinery already in place in your language, these restrictions don't exist and your users don't need to look up what values can be comptime values...they're just another kind of thing I pass to functions, so "of course" I can pass a struct instance.

replies(1): >>43758219 #
40. edflsafoiewq ◴[] No.43756294{12}[source]
No, dependent types depend on runtime values.
replies(1): >>43756340 #
41. edflsafoiewq ◴[] No.43756316{9}[source]
std::enable_if exists to disable certain overloads during overload resolution. Zig has no overloading, so it has no equivalent.
replies(1): >>43756510 #
42. matklad ◴[] No.43756323{10}[source]
>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.

43. matklad ◴[] No.43756340{13}[source]
Yeah, that one Zig can not do, hence "-lite".
replies(1): >>43756634 #
44. matklad ◴[] No.43756510{10}[source]
I'd flip it over and say that C++ has overloading&SFINAE to enable polymorphism which it otherwise can't express.
replies(1): >>43756683 #
45. pcwalton ◴[] No.43756555{12}[source]
That's still just a function of type ∀K∀L.K → L with a bound on K. From a type theory perspective, a comptime argument, when the function is used in such a way as to return a type, is not a value, even though it looks like one. Rather, true or false in this context is a type. (Yes, really. This is a good example of why Zig reusing the keyword "comptime" obscures the semantics.) If comptime true or comptime false were actually values, then you could put runtime values in there too.
46. pcwalton ◴[] No.43756634{14}[source]
The point is that comptime isn't dependent types at all. If your types can't depend on runtime values, they aren't dependent types. It's something more like kind polymorphism in GHC (except more dynamically typed), something which GHC explicitly calls out as not dependent types. (Also it's 12 years old [1]).

[1]: https://www.seas.upenn.edu/~sweirich/papers/fckinds.pdf

47. edflsafoiewq ◴[] No.43756683{11}[source]
Such as? The basic property of overloading is it's open. Any closed set of overloads can be converted to a single function which does the same dispatch logic with ifs and type traits (it may not be very readable).
48. msteffen ◴[] No.43756684{5}[source]
IIUC, it does have code generation—the ability to generate strings at compile-time and feed them back into the compiler.

The argument that the author of TFA is making is that Zig’s comptime is a very limited feature (which, they argue, is good. It restricts users from introducing architecture dependencies/cross-compilation bugs, is more amenable to optimization, etc), and yet it allows users to do most of the things that more general alternatives (such as code generation or a macro system) are often used for.

In other words, while Zig of course didn’t invent compile-time functions (see lisp macros), it is notable and useful from a PL perspective if Zig users are doing things that seem to require macros or code generation without actually having those features. D users, in contrast, do have code generation.

Or, alternatively, while many languages support metaprogramming of some kind, Zig’s metaprogramming language is at a unique maxima of safety (which macros and code generation lack) and utility (which e.g. Java/Go runtime reflection, which couldn’t do the AoS/SoA thing, lack)

Edit Ok, I think Zig comptime expressions are just like D templates, like you said. The syntax is nicer than C++ templates. Zig’s “No host leakage” (to guarantee cross-compile-ability) looks like the one possibly substantively different thing.

replies(1): >>43758236 #
49. naasking ◴[] No.43757580{7}[source]
> Picking some features while leaving others out is something that every language does; if doing that is enough to make a language "revolutionary", then every language is revolutionary.

Picking a set of well motivated and orthogonal features that combine well in flexible ways is definitely enough to be revolutionary if that combination permits expressive programming in ways that used to be unwieldy, error-prone or redundant, eg. "redundant" in the sense that you have multiple ways of expressing the same thing in overlapping but possibly incompatible ways. It doesn't follow that every language must be revolutionary just because they pick features too, there are conditions to qualify.

For systems programming, I think Zig is revolutionary. I don't think any other language matches Zig's cross-compilation, cross-platform and metaprogramming story in such a simple package. And I don't even use Zig, I'm just a programming language theory enthusiast.

> I agree with Walter: Zig isn't doing anything novel.

"Novel" is relative. Anyone familiar with MetaOCaml wouldn't have seen Zig as particularly novel in a theoretical sense, as comptime is effectively a restricted multistage language. It's definitely revolutionary for an industry language though. I think D has too much baggage to qualify, even if many Zig expressions have translations into D.

50. WalterBright ◴[] No.43758219{10}[source]
> Zig has almost no restrictions on what kinds of values can be `comptime` parameters.

Neither does D. The main restriction is the CTFE needs to be pure. I.e. you cannot call the operating system in CTFE (this is a deliberate restriction, mainly to avoid clever malware).

CTFE isn't "special" in D, either. CTFE is triggered for any instance of a "constant expression" in the grammar, and doesn't require a keyword.

51. WalterBright ◴[] No.43758236{6}[source]
> Zig’s “No host leakage” (to guarantee cross-compile-ability) looks like the one possibly substantively different thing.

That is a good idea, but could be problematic if one relies on size_t, which changes in size from 32 to 64 bit. D's CTFE adds checks for undefined behavior, such as shifting by more bits than are in the type being shifted. These checks are not done at runtime for performance reasons.

D's CTFE also does not allow calling the operating system, and only works on functions that are "pure".

52. pcwalton ◴[] No.43758823{10}[source]
> 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".

replies(1): >>43760743 #
53. pron ◴[] No.43760743{11}[source]
> 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?

I don't know D well enough to tell. I'd probably start by looking at how D would do this [1]: https://ziglang.org/documentation/master/#Case-Study-print-i...

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.

replies(1): >>43764684 #
54. TinkersW ◴[] No.43760879{9}[source]
std::enable_if is not the correct comparison, I think you mean "if constexpr"

enable_if is mostly deprecated, and was used for overloading not branching, you can use concepts now instead

55. pron ◴[] No.43761282{7}[source]
> Picking some features while leaving others out is something that every language does; if doing that is enough to make a language "revolutionary", then every language is revolutionary.

You can say that about the design of any product. Yet, once in a while, we get revolutionary designs (even if every feature in isolation is not completely novel) when the choice of what to include and what to leave out is radically different from other products in the same category in a way that creates a unique experience.

56. WalterBright ◴[] No.43764684{12}[source]
> the notion of a comptime variable (for which I couldn't find an analogue in D)

A comptime variable in D would look like:

    enum v = foo(3);
Since an enum initialization is a ConstExpression, it's initialization must be evaluated at compile time.

A comptime function parameter in D looks like:

    int mars(int x)(int y) { ... }
where the first parameter list consists of compile time parameters, and the second the runtime parameters.

D does not have a switch-over-types statement, but the equivalent can be done with a sequence of static-if statements:

    static if (is(T == int)) { ... }
    else static if (is(T == float)) { ... }
Static If is always evaluated at compile time. The IsExpression does pattern matching on types.
replies(1): >>43764796 #
57. pron ◴[] No.43764796{13}[source]
A comptime variable in Zig isn't a constant whose value is computed at compile time (that would just be a Zig constant) but rather variable that's potentially mutable by comptime: https://ziglang.org/documentation/master/#Compile-Time-Varia...

This is one of the things that allow the "comptime language" to just be Zig, as in this example: https://ziglang.org/documentation/master/#Case-Study-print-i...

replies(1): >>43765233 #
58. pcwalton ◴[] No.43765233{14}[source]
You can mutate variables at compile time in D. See the compile time Newton's method example: https://tour.dlang.org/tour/en/gems/compile-time-function-ev...
replies(1): >>43765429 #
59. pron ◴[] No.43765429{15}[source]
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.