6 ms·
> I am comparing Zig to C++ a lot, which many Zig fans would probably take exception to as Zig does not aspire to be a better C++, but rather a better C. I was
by kristoff_it 6y ago
> I am comparing Zig to C++ a lot, which many Zig fans would probably take exception to as Zig does not aspire to be a better C++, but rather a better C.
I was one of those people that started the idea that Zig should be compared to C more than C++. One day I'll express more clearly what I meant by that, but in the meantime I would say that Zig can and should be compared with C++ too.
More generally I think we got to the point where Zig deserves to be analyzed in it's own right and not as a reflection of another language because, among other things, it leads to this kind of misunderstanding:
> I've noticed that compile-time feels like a very lazy environment, meaning that functions do not seem to undergo full semantic analysis unless they are called from somewhere.
Comptime's lazyness is a necessary feature and not an accident. This is how Zig can avoid having a macro system.
More in general the idea is that you are supposed to write tests for your code, which will then ensure your functions get analyzed, and which in turn will produce documentation for your code. This makes testing more core to the development process than it is in other languages.
I'm not saying everyone has to like this approach, but if you stop at the comparison with C, you risk missing how the design of Zig is able to spiral into an new and radical programming experience.
See for example this Twitch clip from yesterday: https://clips.twitch.tv/RockyLivelyChimpanzeeDoubleRainbow-Pov6fVYDbH1_r-A6 https://clips.twitch.tv/RockyLivelyChimpanzeeDoubleRainbow-P...
- aarchi 6y ago> Comptime's lazyness is a necessary feature and not an accident. This is how Zig can avoid having a macro system. Can you elaborate on lazy analysis replacing the need for macros?
- Quekid5 6y agoI'd appreciate some elaboration on that too. It sounds vaguely similar to SFINAE in C++, but I don't know enough about Zig's compilation model to know for sure. (I'm vaguely familar with Zig from a talk by the creator about 1½ years ago, fwiw.)
- defen 6y agoC macros are lazy, but they also operate at the level of lexical tokens instead of the AST, which means you can use macros to generate C code, but you can't really do it in a way that is guaranteed to be safe. With Zig you can have a function where some or all arguments are marked as "comptime", which means the values for those arguments must be known at compile time. Combined with the fact that types can be used as values at compile time means that you can use Zig to generate Zig functions in a safe way.
- dralley 6y agohttps://www.youtube.com/watch?v=Gv2I7qTux7g&t=17m10s https://www.youtube.com/watch?v=Gv2I7qTux7g&t=17m10s
- kristoff_it 6y agoLazyness is what allows you to write code that feels naturally coherent but that would be an error with eager analysis. As an example: switch(build.target.os) { .Linux => std.os.fork(), .Windows => std.os.funcUniqueToWindows(), else => @compileError("feature not supported for target os"), } This a simplified example to say that each path that depends on a comptime condition, such as the target OS, for example, feels intuitively consistent but in Zig types can (and do) mutate depending on those conditions and if the compiler were to eagerly check dead branches it would find plenty of semantical errors. In the stdlib you can see how `os` corresponds to a different struct definition depending on the target: https://github.com/ziglang/zig/blob/master/lib/std/os.zig#L59-L76 https://github.com/ziglang/zig/blob/master/lib/std/os.zig#L5...
- AndyKelley 6y agoThis definitely does cause problems though; I want to acknowledge that. For example, right now we have an issue that auto-generated documentation does not include unreferenced globals. I have some ideas to address this, but it does represent a flaw in the status quo design of the language.
- JacobCarlborg 6y agoIn D, all semantic analysis is performed eagerly (I think), except for templates. D also supports CTFE (Compile Time Function Evaluation), `static if`, `static assert` and a few other language constructs that are evaluated at compile time. I experimented a bit at how D's documentation generator behaves using these language constructs. Here's a snippet of some D code: /// some struct description struct Foo(T) // template { /// some alias description alias Result = int; /// some method description Result foo() { string a = 3; // this does not normally compile } static if (is(T == int)) // evaluated at compile time { /// some method description 2 void bar() {} } version (Windows) { /// some method description for Windows void bazWindows() {} } else version (Posix) { /// some method description for Posix void bazPosix() {} } else static assert(false, "Unsupported platform"); // evaluated at compile time } When generating documentation for the above code, and `Foo` has not been instantiated, the generated docs will include `Foo`, `Result`, `foo`, `bar`, and `bazWindows`. This is regardless of platform. The return type of `foo` will be `Result` and not `int`. This clearly shows that the D compiler doesn't perform semantic analysis when generating documentation. When doing a regular compilation and `Foo` is instantiated, `bar` will only be included if `T` is an `int`. `bazWindows` will only be compiled on Windows and `bazPosix` will only be compiled on Posix platforms. Looking at the implementation, the compiler will generate the docs after semantic analysis and only if there are no errors. But, if `Foo` is never instantiated no errors have occurred so it will continue to generate the docs. On the other hand, if `Foo` is instantiated (and compiles) the compiler will generate docs for the AST after semantic analysis has been performed and `bazWindows` will only be included if the docs were generated on Windows and `bazPosix` will only be included on Posix platforms. What's weird though, is that it seems `bar` will be included regardless of what type `T` is.
- haberman 6y agoI'm not sure how my statement was a misunderstanding if you are confirming that function analysis is in fact lazy.
- kristoff_it 6y agoThe misunderstanding lies in not realizing how lazy evaluation is an integral part of what makes comptime a useful tool and that removing it would break comptime horribly.
- haberman 6y agoThat seems a bit of an overstatement. Calls to sometimes-unavailable functions are but one of the many uses of comptime. It seems entirely possible that lazily-analyzed blocks or functions could be demarcated syntactically (eg. "lazy { }") and that the large majority of comptime evaluation would not need to be inside such a block.
- pron 6y agoTotally. Zig is neither a better C nor a better C++, but an entirely new (and better, IMO) concept for how low-level programming can and should be done. Zig is about as simple as C and as expressive and powerful as C++, but it doesn't follow either's design tradition (unlike Rust, which is very much in the C++ tradition), and should only be compared to either one by virtue of all three being low-level languages.
- fpoling 6y agoWhat is C++ tradition? Is it that one does not pay for a feature unless one uses it? In practice C++ broke with that with exceptions and RTTI. Even Rust does adhere to it since its standard library assumes infallible allocations making one to pay for not relying on it in the form of having to write an alternative library.
- pjmlp 6y agoThat would be the case if one wasn't allowed to turn them off.
- fpoling 6y agoThe standard library of C++ assumes exceptions and that dictates code style that, even if one assumes infallible allocations and disable exceptions, one gets suboptimal code. So even with exceptions off one pays the price either in the form of non-standard library or accepting non-optimal performance. And if one has to prepare for fallible allocations, then the standard C++ is of no use at all in practice.
- pjmlp 6y agoI assume you aren't that comfortable with ISO C++, the standard does allow for failible allocations. That is the role nothrow in placement new, and custom STL allocators as language tool. Additionally plenty of APIs now have exception and error based versions with noexcept.