Zig looks like a really interesting language. I've been keeping my eye on it, waiting excitedly for the 1.x milestone.
Out of curiosity, what's the state of the world regarding Zig's stdlib? While the base language docs seem like they're in a pretty good state, the stdlib is still largely undocumented. Is this because it's unstable? Or is the lack of docs there due to lack of time/resources? Would love to get some clarification. Thanks!
The stdlib documentation is definitely the biggest sore spot of zig for me. It took me a little while to feel comfortable enough digging through the code to start finding what I could do with the stdlib. It slows me down programming using the language significantly.
Yeah, I always check that. The part that says "There is no stdlib documentation yet, but it is planned for the next release" links to a 2015 github issue. That's why I was wondering if it's still a priority or if there were still high level concerns about stability/curation/etc preventing docs from being written.
It's an unwillingness to implement documentation generation twice. Why do it in the c++ compiler when the goal is to become self-hosted?
I think there's a good answer to this which is, "because people really, really need documentation." So I am kicking around ideas of how this can be done without too much duplicate effort.
I see. Do you think there would be value in having someone just manually writing some interim docs (perhaps as a wiki) until the time when it can be properly generated?
This may seem like a weird place to draw experience from, but in my early days of programming, I've found the PHP docs extremely helpful largely thanks to community comments sections giving examples of how to do various things. Just some food for thought.
I think that would be problematic because the standard library is still very unstable. You can see from the release notes just how much changed from 0.4.0. A wiki page would be outdated right away.
One of the tenets of my development process with Zig is "avoid local maximums". A wiki for std lib documentation is not the long term solution to the problem, and so I won't be investing any time into it. All my time is invested in the long term solution.
Perhaps I could see some toy ransomware bootkit demanding the user to pay up or face using your computer with comic neue or comic sans as the default unchangeable font.
Pairs nicely with Fantasque Sans Mono (though if there was a more Comic Sans/Neue-like typeface, I'd set that as my terminal/editor font in a heartbeat).
I might be alone with this thought: Zig seemed really appealing in the beginning, but now I dislike it more and more with each release. It does not seem to keep the simplicity of C at all, it has lots of peculiarities, and it is becoming more and more bloated. I would consider it a C++ replacement at this point, not a C replacement due to its increasing complexity. One of Zig's philosophy was simplicity[1], but I do not think it applies now.
I do get the feeling, but I think part of it is simply that those of us that are used to C tend to underestimate its complexity. Especially when you consider various extensions which are widely used, the preprocessor magic often needed to perform essential tasks, and all the undefined behaviour.
There is really only one thing that I would say goes beyond keeping the language simple as possible: the async stuff. But I think it will probably be totally worth it. The async implementation looks a lot more elegant than anything I've seen so far, and it could be extremely useful in the embedded world, which is one of the places where C is dominant. It might make the language a bit more complicated, but it will make the resulting code much more simpler for a lot of applications, without compromising other goals like safety and efficiency.
Zig is still much, much closer to C than C++. If you don't think so, then you're vastly underestimating the complexity of C++.
debug and safe modes in zig disallow overflows entirely unless you specifically use the wrapping operator to clearly specify your intent. These checks are removed in speed-optimized builds.
In other words, the compiler will enforce that `a + b` does not overflow through all your testing and wherever you explicitly say such checks are required, but turns them into nops for you when you desire speed. You can let the compiler know that you explicitly want wrapping overflow (and not some undefined kind) using the wrapping operator `a +% b`.
These kinds of claims make me so dubious for a language. Also who is trying to switch from C to get an ever-so-slightly performance improvement? That use-case goes to specialized hardware such as FPGAs.
You picked that quote from the main page, not from the release notes, here is what directly follows:
* The reference implementation uses LLVM as a backend for state of the art optimizations.
* What other projects call "Link Time Optimization" Zig does automatically.
* For native targets, advanced CPU features are enabled (-march=native), thanks to the fact that Cross-compiling is a first-class use case.
* Carefully chosen undefined behavior. For example, in Zig both signed and unsigned integers have undefined behavior on overflow, contrasted to only signed integers in C. This facilitates optimizations that are not available in C.
Zig directly exposes a SIMD vector type, making it easy to write portable vectorized code.
So the argument is "exact same compiler as C/C++, but more opportunities for optimization thanks to better semantics and better access to native instructions". This seems reasonable on its face, so care to elaborate on the doubt?
The arguments for not switching from C are often performance or target related, so a language that purports to be an alternative to C would want to point out that those issues aren't a problem.
The reasons to switch away from C are numerous and well documented.
The latter. Zig actually kinda does define this behavior as "`a + b` shall not overflow" and inserts checks in debug and safe builds for it. To get overflow, which zig defines as wrapping overflow, you use a different operator and no check is inserted "`a +% b`". For speed optimized builds, unless you've explicitly told the compiler to insert checks in that scope, it will turn the checks into nops.
So, while it is technically correct to say that it has undefined behavior for overflow, the practical reality is quite different.
We do the same thing in Rust, but I think that characterizing this as UB is misleading, personally. We created a new category, "program error", for this, to distinguish from UB proper.
I'm not sure if Zig inherited the defined/implementation defined/undefined hierarchy from C and C++ though.
Undefined as in undefined by language spec. There are various processor implementations that have different results that are often quite useful. Would you preclude their use?
It's not about the speed being a benefit per-se, but rather not a determent. One of the big reasons people still use C over other languages is that C is perceived to be faster (and indeed usually is).
I would, first of all, call bullshit on this claim. A language can't be "faster than C". Comparing a good implementation with a bad C implementation is where these claims come from.
- to map cleanly with what compilers know to optimize best
- fine control over stack and heap allocation
- has intrinsics / assembly escape hatch
- allows you to specify that pointers don't alias (restrict in C, or default in Fortran)
- gives you prefetching primitives
You will be able to reach hand-tuned Assembly-like performance (and not just C-like performance).
Case in-point, I tuned my own matrix multiplication algorithms in Nim to carefully control register allocations, L1, L2 and L3 cache usage, and vector intrinsics to reach the speed of assembly tuned OpenBLAS and Intel MKL-DNN (no assembly at all):
And matrix multiplication has decades of research and now dedicated hardware (tensor cores, EPU, TPU, NPU, ...) as this is a key algorithm for most numerical workloads.
Why not?
For instance, with current hardware, the good use of SIMD is critical. If you can't write vectorized code in (standard) C while you can do it in another Language, that would be a "faster" language.
Same for other language features that enable faster code: Compile-time computations, better memory model with different pointer aliasing semantic, ...
Yea. It's always funny when people say C is faster than Fortran for scientific computing. I mean, it's true, but only for a very limited amount of people who want to do serious numeric work. C/C++ certainly aren't rare in this area, but Fortran is pleasant to work with for most numeric work and has lots of helpful built-ins and runs close to C in speed.
After a few minutes of trial and error (mostly around convincing Zig's type checker that various things are indeed enum members, and fixing cases where the C version outright ignores return values), I was able to produce a working example:
// curl-test.zig
// Direct hand-translation from curl's simple.c example
usingnamespace @cImport({
@cInclude("stdio.h");
@cInclude("curl/curl.h");
});
pub fn main() anyerror!void {
var curl = curl_easy_init();
var res: CURLcode = undefined;
if(curl != null) {
_ = curl_easy_setopt(curl, CURLoption.CURLOPT_URL,
c"https://example.com");
_ = curl_easy_setopt(curl, CURLoption.CURLOPT_FOLLOWLOCATION, c_long(1));
res = curl_easy_perform(curl);
if(res != CURLcode.CURLE_OK) {
_ = fprintf(stderr, c"curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
}
curl_easy_cleanup(curl);
}
}
Buildable with "zig build-exe curl-test.zig --library c --library curl" (I have to add "-isystem /usr/include --library-path /usr/lib64" to the end of that on Slackware64 14.2; YMMV). Should produce a runnable "curl-test" (or "curl-test.exe") binary executable in the current dir.
For me this is sort of important because languages not having standard coding style end up 10000 coding styles. Rust fmt is an excellent example for avoiding that. Does anybody use Zig + fmt?
Does anyone here have a good understanding of the relative merits of Zig, Crystal, and Nim? Especially compared to higher profile languages like Rust and Go?
All of these languages seem to be statically compiled, and they all seem to promise both performance and safety. Rust and Go are discussed all the time on HN, so I expect most of us here have a reasonable notion of their relative benefits, for example, performance compared to ease of learning.
What about these lesser discussed languages Zig, Nim, and Crystal? Are their runtime performances comparable to one another? Are they equally safe to program in? What are their relative strengths? Or does it just come down to syntax preference?
I think there's value in having a lot of choices to program in, so I'm not asking which language is "best" any absolute sense. We're better off for having all of these languages coexisting. I'm just asking if anyone with experience in the three of them has any insight or impressions of their relative merits.
Zig and Rust both don't have a runtime/GC, they should be on par with performance in C, meaning they are slightly faster than the others (of course this all depends). Go is the only one you listed that doesn't have some kind of parametric polymorphism support (generics). Rust is the only one that will validate your program is memory safe at compile time, they call this 'data race freedom'.
I think in the end it's really up to your tastes which you prefer. Have a look at the tenets of each of the languages and see which resonates with you.
Maybe, but libraries that you use in Nim will probably use the GC, so will many functions in the std library (I imagine). Additionally, the binary you ship will still probably include the runtime & GC. So it's not really fair to say it's on par with C/Rust, there's reasons to avoid GC besides just performance. Say you are writing an embedded application for example. I don't know any Nim, so correct me if I'm wrong.
Having looked at all three, I've so far settled on Zig for a couple reasons:
- Cross-compilation is trivial (I haven't dug too deeply into the cross-compilation story on Nim and Crystal, but Zig's is front-and-center)
- Allocations are explicit (functions that require allocating memory do so by accepting an allocator as an argument)
- Windows support seems to be better than with Crystal (which means I can use Zig for desktop programming for my dayjob, since at work most of the PCs here run Windows 10; not that I would at the moment, given that it's very much not production ready yet, but still)
- I strongly dislike Python's syntax, and therefore strongly dislike Nim's syntax (I also somewhat dislike C's syntax - and therefore Zig's - but not nearly as strongly)
- Zig's build system is pretty great (albeit not exactly intelligent when it comes to finding system header and library paths, I've noticed)
That said, there are some downsides to Zig that I'm hoping can be resolved (whether by someone else or - if I get comfortable enough with it - myself):
- Profound lack of standard library documentation
- I like Crystal's syntax better
- C++ interoperability is practically nonexistent (though it might be possible to write small C++ shims that link against Zig-outputted headers; not sure if Zig's included C compiler can compile those, but if it can, then this would be easy to integrate into the build process)
That is true. I only brought it up as a comparison with e.g. Nim, which does (claim to) have the ability to interoperate with C++ directly without C-ABI shims. D supposedly has similar capabilities, as does Chicken Scheme.
But yeah, the use case ain't to work with C++ code I've written (if I'm writing it, then my goal is to write as close to zero lines of C++ as possible ;) ), but rather to work with third-party C++ codebases that do not expose a C-compatible API (or do so poorly).
I don't know about Crystal but Nim GC is optional and even on a per-type basis:
Use plain object and you are on the stack
Use ptr object and you can use any allocator you want (Nim's default, malloc/free or jemalloc, mimalloc, ...)
Use ref object and your reference will be managed by the GC.
In terms of GC, you the default is deferred reference counting (no ref counting if object is created and destroyed at the end of the scope).
It accepts real-time and max-pause tunings parameters, to allow it to stay beyond those 60FPS / 144 FPS requirements
You can also choose java-like mark-and-sweep, the Boehm GC, the go GC, or no GC and get a warning everytime you try to use a type that uses the GC.
The new async/result location stuff reminds me of the Pin concept that Rust fairly recently adopted. It seems like a pretty huge step forward for the language. Especially since it's casually mentioned that it also solved "safe recursion".
I'm curious about the tools zig offers around doing asyc work while guaranteeing certain invariants, and how the @Frame builtins work with that, but I'm guessing that's going to be elaborated on in a future post.
Zig, Crystal, Nim, Rust, Go. It seems like the industry is responding to the end of moores law and is refocusing on performance. Contrast this with 90's languages like python, ruby, java and c# where computers were expected to get exponentially faster and they focused on developer ergonomics instead.
The @ indicates that the function's built into the compiler itself. Yeah, it looks ugly, but it's nice to be able to visually identify "okay, yeah, this didn't come from any of my imports", and also (I'd assume) avoids cases where one might inadvertently shadow them with their own function definitions.
Re: fn v. function (v. fun v. def v. defun v. defn v. define v. proc v. sub v. label v. the cornucopia of other options here): I don't think it's all that big of a deal. fn does happen to be short, which means more actually-useful information can fit into the same line, so I guess that's nice. The choice of keyword for function definition feels like a really weird criticism.
Out of curiosity, what's the state of the world regarding Zig's stdlib? While the base language docs seem like they're in a pretty good state, the stdlib is still largely undocumented. Is this because it's unstable? Or is the lack of docs there due to lack of time/resources? Would love to get some clarification. Thanks!