Live data from Hacker News

Zig's new plan for asynchronous programs

lwn.net

231–240 of 274 posts

Re: Zig's new plan for asynchronous programs

#231
post #109
post #96

Earlier quoted context omitted.

Getting a simple future from multiple queues and then waiting for the first one is not a match for Go channel semantics. If you do a select on three channels, you will receive a result from one of them, but you don't get any future claim on the other two channels. Other goroutines could pick them up. And if another goroutine does get something from those channels, that is a guaranteed one-time communication and the o…

> channels aren't futures and futures aren't channels. In my mind a queue.getOne ~= a I really do appreciate you being strict about the semantics. Tbh the biggest thing I feel fuzzy on in all this is how go/zig actually go about finding the first completed future in a select, but other than that am I missing something? https://ziglang.org/documentation/master/std/#std.Io.Queue.g...

"but other than that am I missing something?"

I think the big one is that a futures based system no matter how you swing it lacks the characteristic that on an unbuffered Go channel (which is the common case), successfully sending is also a guarantee that someone else has picked it up, and as such a send or receive event is also a guaranteed sync point. This requires some work in the compiler and runtime to guarantee with barriers and such as well. I don't think a futures implementation of any kind can do this because without those barriers being inserted by either the compiler or runtime this is just not a guarantee you can ever have.

To which, naturally, the response in the futures-based world is "don't do that". Many "futures-based worlds" aren't even truly concurrently running on multiple CPUs where that could be an issue anyhow, although you can still end up with the single-threaded equivalent of a race condition if you work at it, though it is certainly more challenging to get there than with multi-threaded code.

This goes back to, channels are actually fairly heavyweight as concurrency operations go, call it two or three times the cost of a mutex. They provide a lot, and when you need it it's nice to have something like that, but there's also a lot of mutex use in Go code because when you don't need it it can add up in price.

Re: Zig's new plan for asynchronous programs

#232

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…

No, async and callbacks in JS are extremely viral. If a function returns a Promise or takes a callback, there is no possible way to execute it synchronously. Hence, coloring.

The reason this coloring isn't a problem for the JS ecosystem, is that it's a single-threaded language by design. So, async/callbacks are the only reasonable way to do anything external to the JS runtime (i.e. reading files, connecting to APIs, etc.)

(notwithstanding that node.js introduced some synchronous external operations in its stdlib - those are mostly unused in practice.)

To put it a different way - yes, JS has function coloring, but it's not a big deal because almost the entire JS ecosystem is colored red anyway.

Re: Zig's new plan for asynchronous programs

#233
post #97

Earlier quoted context omitted.

What about it? It gets called without an Io parameter. Same way that a function that doesn't allocate doesn't get an allocator. I feel like you're trying to set me up for a gotcha "see, zig does color functions because it distinguishes functions that do io and those that don't!". And yes, that's true. Zig, at least Zig code using std, will mark functions that do Io with an Io parameter. But surely you can see how tha…

This creates the drill-down issue we see with React props where we have to pass objects around in the call chain just so that somewhere down the line we can use it. React gets around this with the context hook and which you can access implicitly if it has been injected at a higher level. Do you know if Zig supports something of the sort?

> This creates the drill-down issue we see with React props where we have to pass objects around in the call chain just so that somewhere down the line we can use it.

Oh dear God. That's hell.

Refactoring and plumbing code to change where io happens is going to be a nightmare.

Re: Zig's new plan for asynchronous programs

#234

I think this design is very reasonable. However, I find Zig's explanation of it pretty confusing: they've taken pains to emphasize that it solves the function coloring problem, which it doesn't: it pushes I/O into an effect type, which essentially behaves as a token that callers need to retain. This is a form of coloring, albeit one that's much more ergonomic. (To my understanding this is pretty similar to how Go sol…

AFACT, the only practically critical issue of colored function is the duplication of code b/w sync and async code paths. Zig avoids this with dependency injection, and that’s enough for practical usages (which basically means “ergonomic”). Other points raised by the original article (like calling async function is more difficult) are pretty much unavoidable for the sake of precise control.

Re: Zig's new plan for asynchronous programs

#235
post #189

Earlier quoted context omitted.

This solves a problem for library authors which is that blocking and event-based io implementations of functionality look the same but are not actually the same so users end up complaining when you do one but not the other. It adds a problem of needing to pass the global kind of io through a program. I think this mostly isn’t a huge problem because typical good program design has io on the periphery and so you don’t…

There is nothing special about the [IO a] -> IO [a] in Haskell. You can iterate over it using the "normal" methods of iterating just fine. forM ios $ \io -> io But there are better ways to do it (e.g. sequence), but those are also not "special" to IO in any way. They are common abstractions usable by any Monad.

Haskell is a bit tricky to talk about here because it has other big differences on laziness and suchlike, and this means there are pervasive monads. If you instead consider a language more like JavaScript where async functions return values wrapped in promises and therefore require special versions of lots of things like Array.prototype.forEach for async-returning functions (ok, language feature of generators helps here). The point I’m trying to get at is that putting io-ness into an argument works better than putting it into the return type because it is easier to pass an extra argument when using other language features an harder to do an extra thing with returned values.

Re: Zig's new plan for asynchronous programs

#236
post #14
post #4

I’m excited to see how this turns out. I work with Go every day and I think Io corrects a lot of its mistakes. One thing I am curious about is whether there is any plan for channels in Zig. In Go I often wish IO had been implemented via channels. It’s weird that there’s a select keyword in the language, but you can’t use it on sockets.

One of the harms Go has done is to make people think its concurrency model is at all special. “Goroutines” are green threads and a “channel” is just a thread-safe queue, which Zig has in its stdlib https://ziglang.org/documentation/master/std/#std.Io.Queue

What's the harm exactly?

Re: Zig's new plan for asynchronous programs

#237

Earlier quoted context omitted.

Yes.

Can you explain for those of us less familiar with Haskell (and monads in general)?

Reader monads have been used to implement dependency injection in Haskell and Scala libraries. A monad in general is the ability to compose two functions that have pure arguments and return values that encode some effect... in this case the effect is simply to pass along some read only environment.

Based on my understanding of above, passing an environment as a parameter is not the Reader monad, in fact passing the parameter explicitly through chains of function calls is what the Reader monad intends to avoid in typed, pure functional programming.

Re: Zig's new plan for asynchronous programs

#238
post #62

One thing the old Zig async/await system theoretically allowed me to do, which I'm not certain how to accomplish with this new io system without manually implementing it myself, is suspend/resume. Where you could suspend the frame of a function and resume it later. I've held off on taking a stab at OS dev in Zig because I was really, really hoping I could take advantage of that neat feature: configure a device or sub…

> suspend/resume special @asyncSuspend and @asyncResume builtins, they will be the low level detail you can build an evented io with. new Io is an abstraction over the higher level details that are common between sync, threaded, and evented, so you shouldn't expect the suspension mechanism to be in it.

Oh really? That's perfect.

Re: Zig's new plan for asynchronous programs

#239

Earlier quoted context omitted.

This creates the drill-down issue we see with React props where we have to pass objects around in the call chain just so that somewhere down the line we can use it. React gets around this with the context hook and which you can access implicitly if it has been injected at a higher level. Do you know if Zig supports something of the sort?

> This creates the drill-down issue we see with React props where we have to pass objects around in the call chain just so that somewhere down the line we can use it. Oh dear God. That's hell. Refactoring and plumbing code to change where io happens is going to be a nightmare.

> Refactoring and plumbing code to change where io happens is going to be a nightmare.

I doubt it. It's pretty rare that I want to change something deep in my code to suddenly want to do IO.

This has been a non-issue for me with Zig's approach to Allocators, and I doubt it will become an issue with Io.

Re: Zig's new plan for asynchronous programs

#240
post #74
post #62

One thing the old Zig async/await system theoretically allowed me to do, which I'm not certain how to accomplish with this new io system without manually implementing it myself, is suspend/resume. Where you could suspend the frame of a function and resume it later. I've held off on taking a stab at OS dev in Zig because I was really, really hoping I could take advantage of that neat feature: configure a device or sub…

Can you create a thread pool consisting of one thread, and suspend / resume the thread?

I mean I guess I could... But threads are pretty heavyweight. Though there may be a more lightweight way of implementing them in kernel mode (not coroutines or fibers, but actual threads) and I just have never heard of it.
Post reply on HN