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
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
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!
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.)
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.
Yes. It's exactly that, because Zig very aggressively evaluates values that are known at compile time. It's as simple as:
if (builtin.os.tag == .linux) {
// Everything here only exists if compiling for Linux.
}
The standard library uses this quite a lot for OS/CPU specific things.The same feature is also used for specializing generic containers to specific types (an idea that became famous after the resounding success of std::vector<bool>) so that for example arraylists of u8 expose a Writer but arraylists of f64 do not. Most code that is conditional on T == u8 won't typecheck if T != u8.
I guess more generally these features are used for implementing fmt, json encoders and decoders, etc. None of these thing would work if code conditional on the type had to typecheck for all types.
If it's a comptime constant by "mistake" then it would have resulted in the same wrong behaviour at runtime.
At the end of the day, this is pretty equivalent. Not sure if this is a net gain or not.
Conditional compilation is needed, moving the feature from the preprocessor stage to a lazy evaluator is probably costlier for the compiler and it might also be costlier for the IDE to highlight active/inactive parts of the code.
From the user point of view, this is very close, mostly a cosmetic change.
Moreover, I can grep for preprocessor defines, attributes, and such by name.
There's no way to grep a project for code commented out to prevent execution on some platforms.
How is this better than a line in the language itself?
I don’t know if I consider this a nice thing when thinking about it from a systems language lens. When working at the low level, I like the immediate clarity and compile-time feedback that preprocessor-like checks bring. They’re not perfect, but I think I prefer that instead of relying on laziness.
> 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.
This is correct, but to me, the elephant in the room here though is that this makes it much easier to introduce broken technical debt into a codebase. This likely means that when conducting large-scale refactors in a Zig codebase, as the amount of potentially broken code increases, the count of hair strands remaining in one’s scalp decreases.
Zig is an awesome language, but frankly, I think compiler laziness like this is pretty odd for a statically-typed systems language and not the behavior one would intuitively expect.
All statically compiled languages need to skip compiling such code one way or another. Zig is just thinking this idea to the end and doesn't compile any dead code. I guess the recommended workflow is to have good test coverage. The test basically turns dead code back into live code.
Anyways it's not entirely crazy to think that maybe this sort of thing should be the province of static linters
There's support for invoking with qemu, wine, etc in the build system which allows you to run tests for other platforms.
A case where I've taken advantage if this is to have data structures and code adjust based on the expected page size and cache-line. There are also cases where things may be both comptime and runtime known depending on which platform that you target which is easy to handle by having the maybe comptime value be first in conditional branches.