Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Zig is becoming more production-worthy (zigmonthly.org)
126 points by Shoop on July 5, 2022 | hide | past | favorite | 72 comments


I love how Zig is pushing the state of the art forward. I have some expertise in the area, and I think the future is in languages that prioritize simplicity and developer velocity like Zig is doing.

> The core team will then be able to begin thinking about: ... Exploring hot-code-swapping

Hot code swapping, plus Zig's ability to cross-compile, plus release-safe's memory assistance without mental overhead, all seem to come from one underlying drive: support and empower the programmer without getting in their way.

When designing a language, it's important to keep the user's ultimate goal in mind: making useful software. For some domains that might mean adding restrictions, but for other domains it means getting out of the programmer's way and making language mechanisms and tooling so they can iterate, experiment, and build faster.

I'm particularly excited about Zig's future in webassembly, where many safety concerns are already handled by sandboxing. It's amazing to read that someone ported an entire game to webassembly with Zig.


Looks like it has nice syntax but it seems very very far from pushing state of the art, and that is totally fine.


It can seem like that on the surface, but a closer look reveals a lot of very interesting new features. Here's a few off the top of my head:

* Comptime, which can serve the purpose of generics without a new sub-language [1]

* Colorblind async-await, basically parameterizing async-ness to avoid the downsides of async's infectious nature [2]

* Hot-code swapping proof-of-concept [3] (Edit: specifically, new to low-level languages, AFAICT)

Pretty cool stuff!

[1] https://kristoff.it/blog/what-is-zig-comptime/

[2] https://kristoff.it/blog/zig-colorblind-async-await/

[3] https://www.reddit.com/r/Zig/comments/s9toy1/we_have_a_worki...


Zig's async/await isn't actually colorblind. [1] It just seems that way at first.

[1]: https://gavinhoward.com/2022/04/i-believe-zig-has-function-c...


"Colorblind" is an arbitrary goalpost made to satisfy an imprecisely defined term of one blog post.

The original color article is soo annoying, because it presents two things as one: a JS-specific limitation, and author's arbitrary opinion on how async syntax should (not) look like. Since then even languages that don't have JS's limitation still keep chasing the other point, because otherwise they're shamed for having "color".


"having colors" is an unfortunate property of async/await, not of JS. C# has the same exact set of problems, made even worse by Microsoft's love for horrid APIs and inconsistent runtimes.


Is it a JS-specific limitation? Python works quite similarly.


Make that JS+Python then. OTOH languages that can spawn threads and use synchronization primitives are able to bridge sync and async.


You can spawn threads in Python. Async/await is (among other things) a control-flow primitive for single-threaded programs. C# also includes more or less the same async/await as these languages, as far as I know.

Not only do threads not really do the thing (in part because of the high cost of threads compared to alternatives), the alternative isn't only threads. Users of Lua, Greenlet, ucontext_t, libco, etc. have been able to write single-threaded code that's generic over the async-ness of its callees for decades. Recently there's a trend toward preferring needing to change 99 call sites and function signatures when adding a single piece of I/O to a single function though, needing to write two versions of each library, etc.


I'm not talking about trade-offs of async vs sync. I'm talking about the color article saying languages have "color" when one type of functions (sync) can't use results of another type of functions (async). If you have threads and mutexes, then one type can use the other, regardless of whether that's a good idea or not. The point is that there either is a hard unsolvable api-infecting limitation (like in JS) or there isn't.


Hello, have you had a chance to check out the successful implementation of the program you attempted to write in that blog post? https://gist.github.com/sharpobject/49bd88416e68606d7812d609...


Yes, why does that matter? It still shows that Zig's async/await is not colorblind.


It seemed like most of your post was about not being able to use the two functions through pointers of the same type, but you can use the two functions through pointers of the same type, so maybe now you can delete a majority of your post and submit a retraction to Hacker News or something.

I'm not sure you are using the same notion of "colorblind" as most people. All that is claimed is that much application code can be *compile time generic* over the async-ness of the I/O functions it calls. Stepping up the requirements to being runtime generic introduces some difficult concerns about the different error sets of different I/O functions, but async vs. non-async can be handled as in the above gist or by writing like one switch statement.


I'm new to Zig this year, and I'll share my own learning experience. ziglang.org's overview[0] says, "Zig functions avoid colors." I interpreted that as similar to Go's lack of function colors. Given the rest of the language design, I didn't think it really possible.

Months later I read Gavin's post (linked above). I found that post immensely helpful to understand Zig's design around sync/async function colors (thanks Gavin!). Gavin's post helped to illuminate for me that Zig functions do have colors, but that the compiler can infer the color in most usual cases. This is still very exciting!

As I see it, it's not to Zig's detriment whether it "has" function colors or not, I don't really care. I'm really excited about Zig either way. But I personally (coming from a decade+ of JS/Python/Go) found the verbiage I found used to describe Zig's behavior here confusing.

[0]: https://ziglang.org/learn/overview/


My post has three quotes where people claim Zig is completely colorblind, not just at compile-time. My point stands, especially because of the hoops that gist had to jump through to get it to work.


Erlang has had Hot-code swapping for decades


Dart is one of the only compiled languages that also does that (see Flutter).


Common Lisp as well.


If we ignore Java, .NET, Common Lisp and C++.


Those are not compiled to native code (C++ has hot swapping?), which is the obvious meaning of hot-swapping we're discussing here.


They surely are,you apparently aren't aware of the available toolchains for such purpose.

Plenty of coments of mine where I list what exists since their early days.

Yes, C++ compilers like Visual C++ support it. Or Unreal tooling like Live++.


But you claim Java can hot swap... I happen to know all about hot-swapping in Java, and it's only good for very simple debugging reloads... not at all at the same level as Dart VM. No one who knows Java would claim that.

Common Lisp is only hot-swappable when you're not running a binary, at least from what I know of SBCL.

So, it's hard to take your comment seriously. I am unfamiliar with Visual C++'s hot-swapping capabilities, but to claim the language itself can do it seems pretty ridiculous to me, as all you've got is machine code at runtime, C++ has no hot-swapping runtime and could never have one... which makes me think you're just confused about what we're talking about... I am not talking about debuggers here.


Yes I claim, there are multiple implementation of Java, like JRebel.

Maybe spend some time using proper Lisps like LispWorks and Allegro Common Lisp.

Language and implementation aren't the same thing.

There is so much to learn about compilers, young padawan.


> Common Lisp is only hot-swappable when you're not running a binary, at least from what I know of SBCL.

Which is false.


For every feature X, there is a programming language Y that has had it for decades.

It's not a question of inventing it, but rather of making it mainstream popular.


It's extremely common in plenty of popular languages: Java's a big one, and if you count dynamic languages most do.


I expect that erlang is still significantly more popular than zig.


Of course it is. Zig is an up-and-comer, so most established languages will be more popular at this stage. But Zig aims to be a mostly general purpose language (which is what the majority of mainstream languages are), whereas Erlang does not.


Lisp has had it for decades, full blown graphical workstations were written in it.

Oberon used hot code reload for OS modules.

Visual C++ has had edit-and-continue for years, and hot reload as improvement since the last two years.


RE 3:

Hot-code swapping seems to require a lot of tricky code. Are there compelling use cases that justify the implementation effort?

The examples seem to be mostly focused on swapping out functions but I dot see how it could handle structural changes to data structures.


Hot-code swapping would guarantee a really fast compile times and maybe even "hot reload" of program while it's running. Do not underestimate how much this helps with developer productivity. I'm so tired of the compile times with large C++ codebases...


Exactly! The idea is during development you use the super fast backend that supports hot code swapping. Then when you want to make a release build you use llvm and get all the optimization benefits of a longer compilation.


> I think the future is in languages that prioritize simplicity and developer velocity like Zig is doing.

I kind of dont like Zig because of the syntax its just a little out of the norm from what I'm used to. I like D more if I want to go the C-like route, just hate that its not quite as popular as Go or Rust seem to be. Currently I'm diving into Nim more than anything though, it feels like the sweet spot for me.


One gets used to syntax, don’t let that be the reason you don’t like a language.


It's not just syntax, it is obviously C-like which I like, but it is the way programs look, I think Zig is maybe more low level than I care for.


I wish I had known about D like a decade ago. I swear it was just too ahead of its time.


I've tried it a decade ago, and it wasn't ready back then. It was split between D1 and D2, and Tango and Phobos. That was super annoying, because whatever documentation or library you'd find, it was most likely for a different combination of D+std than you had.

Enjoy D now when it's more mature. The betterC switch is especially interesting.


Its a great language. I suspect the problem was it tried to out java java (GC!) and it kind of tried to out C++ C++


To me simple and not getting in the way are complete opposites of one another. C and go are prime examples. The simplistic nature of the languages mean you can't express many obviously wanted things.


Useful software is good.

But it shouldn't be easy to program in memory errors.


You mean rust shouldn't allow easy to write out of bounds errors?

I'm still waiting for a flag to disable runtime bound checks


I haven't mentioned Rust.

I love KISS and Zig's simplicity especially but there is two sides to programming.

The engineers perspective and the user perspective.

I am both and prefer to have more paid hours and safer software ;)


Zig takes a different approach to safety. For example, Zig has checked arithmetic enabled by default in safe builds, can guarantee static allocation, and can recover from memory allocation failure. It depends on the definition of “safer software”, but for many domains where safety is a field [1], these are also important design decisions.

[1] https://spinroot.com/gerard/pdf/P10.pdf


Zig looks very promising, but there are a few nitpicks I have which keep me from deep diving into it. The most annoying thing I've found so far is treating unused variables as compiler errors. I understand this is something more and more languages are doing, but I can't get over it. It makes writing and prototyping code annoying and tedious, especially during the initial learning phase. Zig trusts the programmer to do things like manually manage their memory, but unused variables is a bridge too far I guess. I don't understand why this isn't a compiler option.

I don't want to leave a purely negative comment so I'll add that the only reason I'm posting in the first place is because, my own personal annoyances aside, the language does look very nice. Especially comptime, I wish every language had a similar construct built in as seamlessly as it is in zig.


If the 'unused variable'-handling is the only thing that holds you back: it's quite simple to build the zig compiler and patch it. See this post by ryuukk in the corresponding issue on github: https://github.com/ziglang/zig/issues/335#issuecomment-10138... and also https://github.com/ziglang/zig/issues/335#issuecomment-10184...

I use this method to build a zig compiler to work with, and when I build a release build of my zig-project I switch to the official compiler and fix the handful 'unused variable' errors. Works fine for me.


Having to build your own version of the compiler to fix this annoyance is like using a sledgehammer to crack a nut. It should be a simple flag, whichever way the default, but it shouldn't require building a compiler.

That's nuts, both in a bad and a good way.


I agree, it should be a flag. Or it should be a warning in debug builds, and an error in release builds.

I'm not sure it's nuts to build the compiler to 'fix' this, though. Customizing and building a tool really isn't such a big deal when you think about it.


I have my own version of the stdlib because I don't think formatting a number with leading zeroes should involve parsing UTF-8. It's pretty easy!

I once said I would like arrays of 33 or more items to be Default and my resident Rustacean told me step 1 is to rebuild libcore after changing the appropriate macro :)


That funny. I have one where octal is banned.


I think the unused error is great, and a lot of people once they got used to it, it becomes quite the useful message, some bugs and some mistakes not easy to catch are quite there now.

Giving it a bit of time is a good way to get used to it and ends generally (at least in my opinion) in better code.

Outside that :shrug:


You do have a point, it is annoying. I’ve written thousands of lines of zig code at this point, and I still frequently have to go back and fix things when the compiler informs me I left a variable or constant unused.

But I think it is less annoying than trying to figure out why things aren’t working at runtime. It’s an interesting trade off, but I appreciate zig trying something different here.


It does make prototyping more annoying but on the other hand it's nice, when reading code, to know that all variables have a reason to be there / aren't "non sequitur"s. It also catches bugs occasionally.

Making it an optional flag has the issue of making it easier to just say "just compile this always with --allow-unused-vars" instead of cleaning up the code. We kinda see that happening with warnings in C/C++.

That said, I too think we should investigate if there's a reasonable middle ground that could be found.


I used Zig for a couple of Advent of Code problems last year. I decided that Zig is not a language I’m going to be using. Here’s why:

- If you ever want to allocate memory, you have to pass in an allocator to the function doing the allocating. You also have to explicitly allocate and deallocate.

- Zig has no concept of a string - it’s just a slice of bytes. This also means Zig has no support for string concatenation, UTF-8, regex, or any of the other many niceties we’ve come to expect as standard.

Zig is not the next Rust or the next C++ - it’s very much the next C.

Zig may be fine for people programming an OS or very highly performance sensitive application. But it’s too low level, and too obtuse about being low-level, for me to ever want to actually use it.


- Passing the allocator gives the user of the container control over where memory is allocated and the scheme used to clean it all up. You can always set a global allocator if you wish, nothing forces you to use it as a parameter.

- Regex can be provided as a library and specialized at comptime. There's never been a need for regex to be part of a primitive type and many other languages do fine without it. Concatenation is a question of "where do you allocate memory" as you have explicit allocation it's not clear what zig should do with it thus the default is to let you call std.mem.concat and dealing with the possible failure or having a look at the problem and asking if you really need concat in the first place. Often you don't as formatting string is better done with std.fmt and ArrayList(u8)'s writer().


> Zig has no concept of a string - it’s just a slice of bytes. This also means Zig has no support for ... regex

I used to think the same thing but it makes sense that for a finite state machine, the matcher doesn't need to work with validated strings at all.

You should read the first 2 paragraphs of burntsushi's (wrote rust regex implementation and a lot more) doc comment here: https://docs.rs/bstr/latest/bstr/#when-should-i-use-byte-str...


>t’s very much the next C

And that is great, once Zig is fully settled down we need something like Objective-Zig, or may be call it Zag.


It's been really great watching zig progress. I'm on the fence on some of its design choices, but I think there's room in this world for zig as a truly modern c.


My benchmark for how serious a C replacement is, is when it successfully moves to not embedded and people actually use it.

So far Rust has the smallest of inroads to embedded.

I hope Zig makes it, but I’ve been down this road before.


I’m excited for Zig’s possible future in embedded, too.

I currently use Nim for our firmware at work, because at the end of the day —compileOnly means it’s just C. It’s been excellent for ESP-IDF/FreeRTOS, but I’d love to see Zig tackle it as well.

One lovely side effect of using Nim is our “business logic” code is remarkably simple in the firmware, as I got PPPoS working nicely with our Cat-M1/LTE NB-IoT modem, so making network requests is literally a single line of standard-library Nim. Quite lovely!

The less we have to write C/C++ the better, and I’d love to see Zig and Rust do a great job in embedded-land!


I've been diving into Nim in my free time after experimenting with Zig and Rust. I really appreciate how clearly I can layout business logic in code without needing to qualify what the compiler should do (my biggest issue with rust is all the stuff you need to do to satisfy the borrow checker I feel obfuscates your intention).

Getting to use Nim for your day job sounds like a dream! I'm particularly keen on Nim's UFCS


> I'm particularly keen on Nim's UFCS

One of my favourite parts about UFCS is how it can turn C libraries that I've had to bind into nice clean looking interfaces!

    esp_err_t sw_enableRx(SwSerial *self, bool State)
Becomes

    proc sw_enableRx*(self: ptr SwSerial, State: bool): esp_err_t {.importc: "$1", header: "<SwSerial.h>".}
Which when called is super lovely!

    var port = sw_open(params, etc)
    port.enableRx(true)


The end result doesn't look anything like C! Really nice.

Nim almost feels like I'm writing a dynamic scripting language, except that I've worked with a typed python code base and it sucked because typing always felt taped on. With generics in Nim I feel like I'm duck typing, but my ducks are compile time checked!

I'm keen on experimenting with protocol oriented programming and as far as I understand, UFCS provides this really neatly to the language. I really like the idea of being able to define my own types and have them seamlessly work with functionality of other libs (and vice-versa) without needing to resort to a language feature like traits.


Absolutely! It's fantastic :)

One piece of advice I can give, is functional programming (which I am a huge proponent of), as defined by a tonne of chained/composed operations on sequences

   .map.filter.etc.etc()
Is not a good fit for Nim. `proc` is a really good reminder: its a procedural language, more than anything else!

You can still use a lot of FP ideas though, but one thing I always remind myself is at the end of the day Nim _is_ C, C is the output, so working within that idea helps a lot in terms of keeping things optimised with little effort on my behalf!


Check out[0] and the Zig Embedded Group[1] - it's not well publicized right now, but there's actually quite a lot of work going on here & Zig seems quite suitable for embedded compared to some other languages.

> The standard library can be used unmodified on freestanding targets thanks to explicit allocation, comptime is extremely useful for things like precalculated lookup tables, and the unfinished C backend (part of the stage 2 compiler) means we’ll be able to target exotic architectures that LLVM can’t.

[0] https://allyourcodebase.com/embedded/

[1] https://github.com/ZigEmbeddedGroup


Still no m68k, sh2, etc?


Both these arch's are contingent on LLVM support, as there is no self-hosted backend (yet). m68k support was merged in LLVM 13, but I haven't seen anyone use it with Zig.


A bunch of people on the discord care about and use the freestanding target, but I don't know much about this.


Wow 40-60 hours at work then 40 on a project at home. I'm lucky to get an hour a week for hobbies.


Is this because of parenthood or something?


Yeah exactly, a wife and child. I'm happy with the deal. It's just an eye opener to see how much time people can dedicate to their craft.

Even when I was single I don't remember having that much time. Chores, making dinner, keeping in touch with friends and family would have left me with less than 40 hours if I'd worked 60 hours.

I'm impressed with their diligence.


I just don't do most of these things (chores, making dinner, keeping in touch with anyone... etc.).


Zig seems like a reasonable fit for WebAssembly for some cases if the memory safety story can be made good enough. (Even though wasm is sandboxed you don't want your in-sandbox app pwned after all)




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: