Live data from Hacker News

Zig's new plan for asynchronous programs

lwn.net

211–220 of 274 posts

Re: Zig's new plan for asynchronous programs

#211

Earlier quoted context omitted.

This is also why function coloring is not a problem, and is in fact desirable a lot of the time.

Exactly, there is nothing wrong with function coloring. It's a design choice. Colored functions are easier to reason about, because potential asynchronicity is loudly marked. Colorless functions are more flexible because changing a function to be async doesn't virally break its interface and the interface of all its callers. Zig has colored functions, and that's just fine. The problem is the (unintentional) gaslighti…

As mentioned, the problem with coloring is not that you see the color, the problem is that you can't abstract over the colors.

Effectful languages basically add user-definable "colors", but they let you write e.g. a `map` function that itself turns color based on its parameter (e.g. becoming async if an async function is passed).

Re: Zig's new plan for asynchronous programs

#212
post #135

Earlier quoted context omitted.

It doesn't and likely never will. This has been a non-issue for years with Allocator. I fail to see why it will be a problem with IO.

What do you mean by non-issue? You just accept passing it around in every function, and now passing around another param for io as well? Or do you create a context struct and pass that around?

> You just accept passing it around in every function

In every function that needs to allocate yes. Sometimes, it'll be stored in a struct, but that's rare. And not every function needs to allocate.

> and now passing around another param for io as well?

Yes. Not everything needs to do Io.

You should try reading some idiomatic Zig code. Ghostty would be an example (as would much of the Zig standard library).

Re: Zig's new plan for asynchronous programs

#213
post #167

Earlier quoted context omitted.

How so? Aside from regular old thread safety issues that is.

This is exactly the problem, thread safety. The function being supplied with std.Io needs to understand what implementation is being used to take precautions with thread safety, in case a std.Io.Threaded is used. What if this function was designed with synchrony in mind, how do you prevent it taking a penalty guarding against a threaded version of IO?

The function being called has to take into account thread safety anyway even if it doesn't do IO. This is an entirely orthogonal problem, so I can't really take it seriously as a criticism of Zig's approach. Libraries in general need to be designed to be thread-safe or document otherwise regardless of if the do IO, because a calling program could easily spin up a few threads and call it multiple times.

> What if this function was designed with synchrony in mind, how do you prevent it taking a penalty guarding against a threaded version of IO?

You document it and state that it will take a performance penalty in multithreaded mode? The same as any other library written before this point.

Re: Zig's new plan for asynchronous programs

#214

Earlier quoted context omitted.

If calling the same function with a different argument would be considered 'function coloring', every function in a program is 'colored' and the word loses its meaning ;) Zig actually also had solved the coloring problem in the old and abandondend async-await solution because the compiler simply stamped out a sync- or async-version of the same function based on the calling context (this works because everything is a…

In that case JS is not colored either because an async function is simply a normal function that returns a Promise. As far as I understand, coloring refers to async and sync functions having the same calling syntax and interface, I.e. b = readFileAsync(p) b = readFileSync(p) share the same calling syntax. Whereas b = await readFileAsync(p) readFileAsync(p).then(b => ...) b = readFileSync(b) are different. If you have…

> In that case JS is not colored either because an async function is simply a normal function that returns a Promise.

Exactly, IMHO at least, JS doesn't suffer from the coloring problem because you can call async functions from sync functions (because the JS Promise machinery allows to fall back to completion callbacks instead of using await). It's the 'virality' of await which causes the coloring problem, but in JS you can freely mix await and completion callbacks for async operations).

Re: Zig's new plan for asynchronous programs

#215

Earlier quoted context omitted.

In that case JS is not colored either because an async function is simply a normal function that returns a Promise. As far as I understand, coloring refers to async and sync functions having the same calling syntax and interface, I.e. b = readFileAsync(p) b = readFileSync(p) share the same calling syntax. Whereas b = await readFileAsync(p) readFileAsync(p).then(b => ...) b = readFileSync(b) are different. If you have…

> In that case JS is not colored either because an async function is simply a normal function that returns a Promise. Exactly, IMHO at least, JS doesn't suffer from the coloring problem because you can call async functions from sync functions (because the JS Promise machinery allows to fall back to completion callbacks instead of using await). It's the 'virality' of await which causes the coloring problem, but in JS…

await isn't viral per se, it's a purely local transformation. The virality is from CPS/callbacks and Promise.

Re: Zig's new plan for asynchronous programs

#216

Earlier quoted context omitted.

If calling the same function with a different argument would be considered 'function coloring', every function in a program is 'colored' and the word loses its meaning ;) Zig actually also had solved the coloring problem in the old and abandondend async-await solution because the compiler simply stamped out a sync- or async-version of the same function based on the calling context (this works because everything is a…

Let's revisit the original article[1]. It was not about arguments, but about the pain of writing callbacks and even async/await compared to writing the same code in Go. It had 5 well-defined claims about languages with colored functions: 1. Every function has a color. This is true for the new zig approach: functions that deal with IO are red, functions that do not need to deal with IO are blue. 2. The way you call a…

> This is also true for Zig: Red functions require an Io argument. Blue functions do not. Calling a red function means you need to have an Io argument.

I don't think that's necessarily true. Like with allocators, it should be possible to pass the IO pointer into a library's init function once, and then use that pointer in any library function that needs to do IO. The Zig stdlib doesn't use that approach anymore for allocators, but not because of technical restrictions but for 'transparency' (it's immediately obvious which function allocates under the hood and which doesn't).

Now the question is, does an IO parameter in a library's init function color the entire library, or only the init function? ;P

PS: you could even store the IO pointer in a public global making it visible to all code that needs to do IO, which makes the coloring question even murkier. It will be interesting though how the not-yet-implemented stackless coroutine (e.g. 'code-transform-async') IO system will deal with such situations.

Re: Zig's new plan for asynchronous programs

#217
jm2c, never had an issue with coloured functions, as long as they are tracked at the type level and you know what you're getting.

Yes, eventually you're gonna lift sync to async code, and that works fine as it is generally also the runtime model (asynchronous, event-based).

Re: Zig's new plan for asynchronous programs

#218

Earlier quoted context omitted.

> If calling the same function with a different argument would be considered 'function coloring', than every function in a program is 'colored' and the word loses its meaning ;) I mean, the concept of "function coloring" in the first place is itself an artificial distinction invented to complain about the incongruent methods of dealing with "do I/O immediately" versus "tell me when the I/O is done"--two methods of I/…

My understanding of this design is that you can write the logic separately from the decision to "do I/O immediately" versus "tell me when the I/O is done" You can write a parser thats outputs a DOM and run it on a stream, or write a parser with a streaming API and run it synchronously on a buffer. You should pick the optimal tool for the situation, but there is no path dependence anymore.

Honestly I don't see how that is different than how it works in Rust. Synchronous code is a proper subset of asynchronous code. If you have a streaming API then you can have an implementation that works in a synchronous way with no overhead if you want. For example, if you already have the whole buffer in memory sometimes then you can just use it and the stream will work exactly like a loop that you would write in the sync version.

Re: Zig's new plan for asynchronous programs

#219
post #71

Earlier quoted context omitted.

yes, you can: runtime.block_on(async { }) https://play.rust-lang.org/?version=stable&mode=debug&editio...

Here's a problem with that: Cannot start a runtime from within a runtime. This happens because a function (like `block_on`) attempted to block the current thread while the thread is being used to drive asynchronous tasks. https://play.rust-lang.org/?version=stable&mode=debug&editio...

Right, because this would deadlock. But it seems like Zig would have the same issue. If I am running something in a evented IO system and then I try and do some blocking IO inside it then I will get a deadlock. The idea that you can write libraries that are agnostic to the asynchronous runtime seems fanciful to me beyond trivial examples.

Re: Zig's new plan for asynchronous programs

#220
post #186

Earlier quoted context omitted.

Having used zig a bit as a hobby. Why is it more ergonomic? Using await vs passing a token have similar ergonomics to me. The one thing you could say is that using some kind of token makes it dead simple to have different tokens. But that's really not something I run into often at all when using async.

Making it dead simple to have different tokens is exactly the goal. A smattering of examples recently on my mind: As a background, you might ask why you need different runtimes ever. Why not just make everything async and be done with it, especially if the language is able to hide that complexity? 1. In the context of a systems language that's not an option. You might be writing an OS, embedded code, a game with atyp…

> If it were written with async it would likely have enough other baggage that it wouldn't fit or otherwise wouldn't work

I'm unclear what this means. What is the other baggage in this context?

Post reply on HN