8 ms·
Under "External Open Source Projects Using LLVM 7": > Zig is an open-source programming language designed for robustness, optimality, and clarity. Zig is an al
by speps 8y ago
Under "External Open Source Projects Using LLVM 7":
> Zig is an open-source programming language designed for robustness, optimality, and clarity. Zig is an alternative to C, providing high level features such as generics, compile time function execution, partial evaluation, and LLVM-based coroutines, while exposing low level LLVM IR features such as aliases and intrinsics. Zig uses Clang to provide automatic import of .h symbols - even inline functions and macros. Zig uses LLD combined with lazily building compiler-rt to provide out-of-the-box cross-compiling for all supported targets.
Nice shout out :)
- azhenley 8y agoZig needs a Wikipedia page! It seems like a competitor for Go or Rust? I've been looking into these languages a lot lately, but Zig looks interesting... I wish the website was better at showing off examples and the big features for the language though.
- simias 8y agoI'm a C coder, a big fan of Rust and while I've heard of Zig before I still don't really get what it's going for. As far as I can tell it's Rust without the borrow checker and the mental overhead that goes with it. Maybe it's because I'm wearing my Rust glasses but some design choices seem a bit strange to me, for instance regarding Zig's unions (equivalent to Rust's enums or C's tagged unions): https://ziglang.org/documentation/master/#union https://ziglang.org/documentation/master/#union const Payload = union { Int: i64, Float: f64, Bool: bool, }; [...] var payload = Payload {.Int = 1234}; // payload.Float = 12.34; // ERROR! field not active assert(payload.Int == 1234); // You can activate another field by assigning the entire union. payload = Payload {.Float = 12.34}; assert(payload.Float == 12.34); [...] // Unions can be given an enum tag type: const ComplexTypeTag = enum { Ok, NotOk }; const ComplexType = union(ComplexTypeTag) { Ok: u8, NotOk: void, }; [...] const c = ComplexType { .Ok = 0 }; assert(ComplexTypeTag(c) == ComplexTypeTag.Ok); That's weird ergonomics IMO and quite a lot of typing. As far as I can tell there's no type inference? Also I'm not sure why const is used everywhere, even for things like type declarations, does Zig allow messing with types at runtime? That seems like a pretty high level language feature so it's quite interesting if it does. Anyway, from what I see it's basically somewhat halfway between C and Rust, with some of the syntactic sugar of Rust but not the borrow checker and everything that goes along with it. That being said the language is still very young and at this stage Rust was a bit of a mess as well, what with the numerous pointer types, green threads and the like... Maybe as they progress toward 1.0 they'll be able to expose a clearer picture of what they're doing.
- AndyKelley 8y agoI'd be interested in seeing a code comparison of equivalent Rust and Zig code with regards to tagged unions, to demonstrate your claim. I could help you with the Zig code if you provide the Rust code. Also to answer your questions: > As far as I can tell there's no type inference? You can see an example of type inference here: https://ziglang.org/documentation/master/#Hello-World https://ziglang.org/documentation/master/#Hello-World > does Zig allow messing with types at runtime? Yes. https://ziglang.org/documentation/master/#Generic-Data-Structures https://ziglang.org/documentation/master/#Generic-Data-Struc... Edit: my mistake, I misread or didn't see the "at runtime" part. It only allows messing with types at compile-time.
- simias 8y ago>I'd be interested in seeing a code comparison of equivalent Rust and Zig code with regards to tagged unions, to demonstrate your claim. I could help you with the Zig code if you provide the Rust code. Well in Rust you don't have to define the enum and the union separately, actually in Rust unions and enums are effectively the same thing (outside of C FFI that is). So you in Rust you have something like: enum ComplexType { Ok(u8), NotOk, } Then to unpack it you always have to destructure it, you can't just do my_complex_type.Ok() or something like that. You can create accessor methods if that makes sense for your enum though, for instance for this ComplexType you could implement an "unwrap" function that returns the u8 in case of success and panics in case of an error. In general you tend to avoid those unless they really make sense, as a rule of thumb it's better to "match" the type (which seems similar to Zig's switch statement with enums). Beyond that I dug a bit deeper in the docs and I don't quite understand how generics are supposed to work exactly, at first they look like what I'm used to in other languages but then I get to the generic printf example and I see this code: pub fn printValue(self: *OutStream, value: var) !void { const T = @typeOf(value); if (@isInteger(T)) { return self.printInt(T, value); } else if (@isFloat(T)) { return self.printFloat(T, value); } else { @compileError("Unable to print type '" ++ @typeName(T) ++ "'"); } } Is it just for the sake of the example or does it just exhibit a limitation of the generic system? What would happen if I wanted to be able to print a custom type for instance? Also, if generics exist, why are things like "@clz" or "@intToFloat" compiler builtins, surely they could be implemented as part of the stdlib using a generic method? Although I see that "@memset" and "@memcpy" are also builtin and I can't quite figure out why so maybe I'm missing something here.
- ksec 8y agoI never view Go as a competitor to C, and Rust is more of an competitor to C++. Zig reads to me as something actually trying to displace C. Or C with no corner cases and some modern features.
- IshKebab 8y agoGo is definitely a competitor to C, but only in places where you can tolerate a garbage collector. Zig seems to be a competitor to C in places where you can't.
- knocte 8y agoI disagree, Go ignores errors by default. No sane C dev would accept that as an "upgrade".
- detaro 8y agoIn what way does C not ignore errors?
- skybrian 8y agoWhat do you mean? Error checking for C is very inconsistent (sentinel values and errno), so surely that's worse?
- simias 8y agoI think they mean that C coders have wrestled so much with the language's terrible error handling mechanics (or lack thereof) that it's probably one of the first thing they'd want fix when "upgrading" languages. I would tend to agree.
- knocte 8y agoUpgrading error handling is great, but Go has the worst approach on doing it.
- rurban 8y agoIt needs one, but I won't argue with the editor-morons there, who doubt any usefulness on any serious project. I can still remember how often nimrod was deleted, and then nim. and others I won't mention in their cleanup destruction rampages. We are the experts, not the Wikipedia editors, but they are treating it as their personal baby, and you should not interfere.
- amelius 8y agoDoes it do tail call optimization?
- AndyKelley 8y agoit does, the same as how it does in clang with -O3. There's an open issue for adding guaranteed tail call semantics: https://github.com/ziglang/zig/issues/694 https://github.com/ziglang/zig/issues/694 it's an important issue for the project because one of the things it is tackling is the problem of unbounded stack growth. zig wants to get to a state where upper bound stack size is known at compile-time.
- tinus_hn 8y agoThat looks to me like an NP complete problem.
- adwn 8y agoNot if the set of answers includes "couldn't determine an upper bound". Many (but not all) problems become a whole lot easier if you restrict them to sane inputs. Determining an upper bound on stack size is especially interesting for, e.g., embedded software, and the intersection of "sane embedded program" and "program with complex stack size behavior" is rather small.
- SolarNet 8y agoSpecifically if features like function pointers are avoided, and tail call optimized recursive data structure functions are used. Hell for embedded uses if it can just determine it for some functions, and not others (like say anything involving dispatch) it would still be very useful. From writing: TaskCreate(&some_func, 2048 /* Hope this is enough stack */) To: TaskCreate(&some_func, sizeof(some_func)) would be amazing.
- fpoling 8y agoOriginal Fortran compilers were able to tell exactly how much stack any program was using. The answer was zero as all local variables were allocated statically and recursive calls were not supported. But one does not need to go to such extreme as many useful code patterns can be proven to be stack bounded and for the rest of code the compiler may require runtime checks or some annotations like Rust’s unsafe.
- kvark 8y agoI'd be interested to hear out experience reports of people who tried to implement the same thing on C/Rust/Zig, like this one: https://github.com/isaachier/gbemu https://github.com/isaachier/gbemu