7 ms·
This is very different to most static languages. From the article: The whole process is lazy — only things transitively used from main are analyzed. Compiler
by nickcw 4y ago
This is very different to most static languages.
From the article:
The whole process is lazy — only things transitively used from main are analyzed. Compiler won’t complain about something like
fn unused() void {
1 + "";
}
This looks perfectly fine at the Zir level, and the compiler will not move beyond Zir unless the function is actually called somewhere.
- planede 4y agoYeah, it looks like Zig is a bit overly lazy. As a comparison, what C++ does is that it differentiates between "dependent" and "non-dependent" expressions, where "dependent" means that it depends on some template parameter (analogous to comptime parameter here). "non-dependent" expressions are type-checked eagerly, while dependent expressions are only checked at instantiation time. The design is certainly more complicated, even more so when you consider function overloading and name lookup, which can give different results if done late or early. But there are some upsides, and zig wouldn't need to deal with the additional complications of overloading. edit: https://eel.is/c++draft/temp.dep https://eel.is/c++draft/temp.dep
- skywal_l 4y agoWell it's basically like C macros. The C compiler won't see any code that's behind a deactivated macro. Same thing for zig here, except you don't have to deal with an unreadable macro language full of pitfalls. All is done in the language itself.
- stephc_int13 4y agoGetting rid of macros (and all of their well documented pitfalls) is good. Getting rid of the preprocessor and conditional compilation may not have been necessary. (as the feature is needed anyway and have to be implemented in a different manner) The thing is that comptime, even if better than macros because everything is written in the same language, might end up not being much better than C macros. Many of the macro pitfalls still applies, if you think about it...
- anonymoushn 4y agoI sort of like that Zig is lazy here, because in C++ a bunch of stuff that works in Zig just doesn't work. In this line[0] a radix sort selects a function to read the next 8 or 16 bits of the input words. The C++ version of this code has to have readOneByte and readTwoBytes take an additional template parameter to control the return type: static constexpr auto readBucket = (BYTES_PER_LEVEL == 2) ? readTwoBytes<idx, udigit> : readOneByte<idx, udigit>; because both parts of the expression are evaluated by the compiler and must be the same type, including the function's return type. This means we end up instantiating nonsense functions like readTwoBytes returning u8. Elsewhere in the same file, some should-be-impossible template instantiations have had to have their static_assert's removed, because C++ will instantiate them and then not insert any calls to them into the generated code. So one cannot use static_assert to say "If this is reachable, that's a bug, please fail compilation and return an error" because template instantiation and the execution of the static_assert does not imply that the function won't be immediately discarded as unreachable. [0]: https://github.com/alichraghi/zort/blob/main/src/radix.zig#L161 https://github.com/alichraghi/zort/blob/main/src/radix.zig#L...
- planede 4y agoThis is a different issue. You could have replaced the ternary with if constexpr in a lambda, but it's very verbose. I saw some people to propose that ternary should work potentially like if constexpr, if the condition is a constant expression. Honestly, IMO the ternary operator is already cursed, and it should not be overloaded with more responsibility. I think this should work without nonsense return types: static constexpr auto readBucket = []{ if constexpr (BYTES_PER_LEVEL == 2) { return readTwoBytes<idx, udigit>; } else { return readOneByte<idx, udigit>; } }();
- mdaniel 4y agoThat is starkraving insanity, and now highlights why there is a whole submission about making a Zig IDE. So I guess if one wanted to do any refactoring, be sure to go through and uncomment every code flow in the app first And look, I can appreciate lazy evaluation being awesome in some circumstance, but `fn wat() void { 1 + ""; }` just spits in the face of static typing
- dilap 4y agoA nice thing you get in return tho is for separate platforms you can disable code just in the language itself; it doesn't need to be a preprocessor or or handled magically in the build system. It's also not nearly as bad as a dynamically typed language: you might have broken code you won't realize until you try to use it, but you'll always realize at compile-time. In dynamic languages you refactor and don't notice you broke stuff until runtime!
- dleslie 4y agoUncommenting a line for a platform-specific feature isn't really any more difficult than decorating a function, adding a pre-processor gate, or any of the other normal ways of doing it.
- dilap 4y agomore difficult maybe maybe not, but the way zig does it is simpler in that it's fewer languages you have to know: you don't have to learn the c preprocessor (c/c++) or about magic +build tags (Go) or whatever; it's just normal conditionals in the same language. you might [reasonably] say you can't care about that kind of simplicity, which is fine; but radical simplicity is definitely one of the major charms of zig. (and you don't use the zig feature by commenting/uncommenting, it's via inspection of the environment/flags in the comptime code.)
- Kwpolska 4y agoSo how does Zig handle platform-specific stuff that cannot be compiled on other platforms? Is it an `if` resolved at compile time causing the uncompileable code to be unused and disappear after the Zir stage? You don't need a full preprocessor with macros and other evil stuff. Just #define and #ifdef would solve this without allowing wrong code that randomly disappears. And learning and understanding these two directives requires zero mental effort.
- Kukumber 4y agoWhy should the compiler compile code that is not called/exported? that's a waste of processing power and therefore a waste of time At best it should give you a warning that the function is not used, that's it
- gavinhoward 4y agoBecause the parser still has to skip it, especially if it's in a file with code that is used. The parser needs to know the beginning and the end to do so. This takes at least rudimentary parsing. Oh, and the compiler may not know it's unused until it processes all files if it's a public function.
- nickcw 4y agoI found it a surprise. However it is really useful when using comptime and doing those really awkward cross platform things (like delving into the innards of a stat_t) - your FreeBSD stuff with its different member names will be completely ignored on Linux.
- titzer 4y agoThe Virgil compiler will typecheck all code given to it on the command-line and then only compile what is reachable from main() after running initialization in the internal interpreter, both code and data. Unused functions and fields and classes will simply never be visited for codegen.
- gavinhoward 4y agoThis is my biggest complaint against Zig's comptime. I think it's possible to do comptime better, by putting comptime into separate files that can then "export" generated code.
- junon 4y agoThis is exactly how Rust's proc macros work.
- gavinhoward 4y agoThis is what I suspected.