Most active commenters
  • pron(5)
  • WalterBright(5)
  • edflsafoiewq(3)

←back to thread

Things Zig comptime won't do

(matklad.github.io)
458 points JadedBlueEyes | 15 comments | | HN request time: 0.001s | 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 #
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 #
1. 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 #
2. 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 #
3. pron ◴[] No.43755065[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 #
4. WalterBright ◴[] No.43755336{3}[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 #
5. pron ◴[] No.43755439{4}[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 #
6. WalterBright ◴[] No.43755658{5}[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 #
7. pcwalton ◴[] No.43755843{6}[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 #
8. pron ◴[] No.43756052{7}[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 #
9. edflsafoiewq ◴[] No.43756175{7}[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.
10. sekao ◴[] No.43756271{8}[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 #
11. edflsafoiewq ◴[] No.43756316{8}[source]
std::enable_if exists to disable certain overloads during overload resolution. Zig has no overloading, so it has no equivalent.
replies(1): >>43756510 #
12. matklad ◴[] No.43756510{9}[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 #
13. edflsafoiewq ◴[] No.43756683{10}[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).
14. WalterBright ◴[] No.43758219{9}[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.

15. TinkersW ◴[] No.43760879{8}[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