4 ms·
As a disclaimer, the last time I gave Zig a solid shot was when 0.12 released. The last time I played with comptime properly was in 0.11. There's a heap of pra
by dminik 2y ago
As a disclaimer, the last time I gave Zig a solid shot was when 0.12 released. The last time I played with comptime properly was in 0.11.
There's a heap of praise thrown at zig comptime. I can certainly see why. From a programming language perspective it's an elegant and very powerful solution. It's a shame that Rust doesn't have a similar system in place. It works wonderfully if you need to precompute something or do some light reflection work.
But, from an actual user perspective it's not very fun or easy to use as soon as you try something harder. The biggest issue I see is that there's no static trait/interface/concept in the language. Any comptime type you receive as a parameter is essentially the `any` type from TypeScript or `void` from C/C++. If you want to do something specific* with it, like call a specific method on it, you have to make sure to check that the type has it. You can of course ignore it and try to call it without checking it, but you're not going to like the errors. Of course, since there are no interfaces you have to do that manually. This is done by reading the Zig stdlib source code to figure out the type enum/structures and then pattern-matching like 6 levels deep. For every field, every method, every parameter of a method. This sucks hard. Of course, once you do check for the type you still won't get any intellisense or any help at all from your IDE/editor.
Now, there are generally two solutions to this:
One would be to add static interfaces/concepts to the language. At the time this was shot down as "unnecessary". Maybe, but it does make this feature extremely difficult to use for anyone but the absolutely most experienced programmers. Honestly, it feels very similar to how Rust proc macros are impenetrable for most people.
The second one is to take a hint from TypeScript and take their relatively complex type system and type assertions. Eg. `(a: unknown): a is number => typeof a === 'number'`.
This one also seems like a bust as it seems to go against the "minimal language" mantra. Also, I don't get the feeling that the language dev team particularly cares about IDEs/LSPs as the Zig LSP server was quite bad the last time I tried it.
Now, the third solution and the one the people behind the Zig LSP server went with is to just execute your comptime functions to get the required type information. Of course, this can't really make the experience of writing comptime any easier, just makes it so that your IDE knows what the result of a comptime invocation was.
So in short it is as difficult to use as it is cool. Really, most of the language is like this. The C interop isn't that great and is severly overhyped. The docs suck. The stdlib docs are even worse. I guess I'm mostly dissapointed since I was hoping Zig could be used where unsafe Rust sucks, but I walked away unsatisfied.
- flohofwoe 2y agoRegarding the 'missing interface feature' it's quite trivial to write a generic comptime function which checks whether an input value has the expected shape by checking it against a struct type which describes the interface (basically a comptime typeguard), such a helper function could probably go into std.meta and would provide better error messages then the followup compile errors - won't necessairly help with the LSP problem though. Whether that's a good or bad idea remains to be seen (usually I prefer syntax sugar over such a building blocks system - reminds me too much of the C++ stdlib approach), but a surprising amount of dedicated typesystem features in other languages can be done with comptime coding in Zig.
- dminik 2y agoYeah, that should be possible and is something I considered when I was playing around with comp time. But, having a language-provided or even recommended way makes things much easier. Also, I wouldn't call it trivial. I think you need to deal with recursively comparing types and also need to handle Self and other quirks. It kind of reminds me of the other side of static/dynamic polymorphism in that much of the language lives only on undocumented conventions. At some point, the folks writing Zig decided that they really needed some runtime polymorphism for their stdlib implementation. Of course, this would be implemented in the language (using dyn traits in rust, or OOP in C++) in every other langauge. Ok, but this is Zig, so maybe it's implemented as a std.meta helper in the stdlib? Of course not. Instead you have to figure out what the stdlib does, which is of course to implement it manually for every type that needs it. Making it worse is that at some point the way to do this has changed and so at least at the time there were actually two different ways to do dynamic dispatch in the stdlib and other people's code/tutorials/whatever. What a mess of a language. So many good ideas hindered by baffling decisions. Writing Zig is like grinding teeth.
- naasking 2y ago> If you want to do something specific* with it, like call a specific method on it, you have to make sure to check that the type has it. You can of course ignore it and try to call it without checking it, but you're not going to like the errors. "Better errors" seems like something that should be fixable in the compiler, eg. by tracking whether a variable was initialized with a static or comptime type, and if the latter, then triggering a different error path to tracing back to the comptime type variable.