Live data from Hacker News

Zig's New Async I/O

kristoff.it

91–100 of 293 posts

Re: Zig's New Async I/O

#91

Although I'm not wild about the new `io` parameter popping up everywhere, I love the fact that it allows multiple implementations (thread based, fiber based, etc.) and avoids forcing the user to know and/or care about the implementation, much like the Allocator interface. Overall, I think it's a win. Especially if there is a stdlib implementation that is a no overhead, bogstock, synchronous, blocking io implementatio…

Isn’t “don’t pay for what you don’t use” a myth? Some other person will using unless you are a very small team with discipline, and you will pay for it.

Or just passing around an “io” is more work than just calling io functions where you want them.

Re: Zig's New Async I/O

#92
post #14
post #3

I feel that I have to point this out once again, because the article goes so far as to state that: > With this last improvement Zig has completely defeated function coloring. I disagree with this. Let's look at the 5 rules referenced in the famous "What color is your function?" article referenced here. > 1. Every function has a color Well, you don't have async/sync/red/blue anymore, but you now have IO and non-IO fun…

Go also suffers from this form of “subtle coloring”. If you’re working with goroutines, you would always pass in a context parameter to handle cancellation. Many library functions also require context, which poisons the rest of your functions. Technically, you don’t have to use context for a goroutine and could stub every dependency with context.Background, but that’s very discouraged.

Having all async happen completely transparently is not really logically possible. asynchronous logic is frequently fundamentally different from synchronous logic, and you need to do something different one way or the other. I don't think that's really the same as "function colouring".

And context is used for more than just goroutines. Even a completely synchronous function can (and often does) take a context, and the cancellation is often useful there too.

Re: Zig's New Async I/O

#93
post #15

Seeing a systems language like Zig require runtime polymorphism for something as common as standard IO operations seems off to me -- why force that runtime overhead on everyone when the concrete IO implementation could be known statically in almost all practical cases?

Runtime polymorphism isn’t something inherently bad.

It is bad if you are introducing branching in a tight loop or preventing compiler from inlining things it would inline otherwise and other similar things maybe?

Re: Zig's New Async I/O

#94
post #3

I feel that I have to point this out once again, because the article goes so far as to state that: > With this last improvement Zig has completely defeated function coloring. I disagree with this. Let's look at the 5 rules referenced in the famous "What color is your function?" article referenced here. > 1. Every function has a color Well, you don't have async/sync/red/blue anymore, but you now have IO and non-IO fun…

> In order to call such a function you also need to provide the context. Zig hasn't really solved this.

It is much more flexible though since you don't need to pass the IO implementation into each function that needs to do IO. You could pass it once into an init function and then use that IO impl throughout the object or module. Whether that's good style is debatable - the Zig stdlib currently has containers that take an allocator in the init function, but those are on the way out in favour of explicitly taking the allocator in each function that needs to allocate - but the user is still free to write a minimal wrapper to restore the 'pass allocator into init' behaviour.

Odin has an interesting solution in that it passes an implicit context pointer into each function, but I don't know if the compiler is clever enough to remove the overhead for called functions that don't access the context (since it also needs to peek into all called functions - theoretically Zig with it's single-compilation-unit approach could probably solve that problem better).

Re: Zig's New Async I/O

#95
post #19

Earlier quoted context omitted.

To me, there's no difference between the IO param and async/await. Adding either one causes it to not be callable from certain places. As for the second thing: You can do that, but... You can also do this in Rust. Yet nobody would say Rust has solved function coloring. Also, check this part of the article: > In the less common case when a program instantiates more than one Io implementation, virtual calls done throug…

>To me, there's no difference between the IO param and async/await. You can't pass around "async/await" as a value attached to another object. You can do that with the IO param. That is very different.

Sure you can. An `async` function in Javascript is essentially a completely normal function that returns a promise. The `async`/`await` syntax is a convenient syntax sugar for working with promises, but the issue would still exist if it didn't exist.

More to the point, the issue would still exist even if promises didn't exist — a lot of Node APIs originally used callbacks and a continuation-passing style approach to concurrency, and that had exactly the same issues.

Re: Zig's New Async I/O

#96

As the author of a semi-famous post about how Zig has function colors [1], I decided to read up on this. I see that blocking I/O is an option: > The most basic implementation of `Io` is one that maps to blocking I/O operations. So far, so good, but blocking I/O is not async. There is a thread pool that uses blocking I/O. Still good so far, but blocking I/O is still not async. Then there's green threads: > This implem…

Their bet seems to be that they can transparently implement real async inside an IO implementation using compiler magic. But then it means if you use that IO instance with the magic then your function gets transformed into a state machine?

Then this whole thing is useless for implementing cooperative scheduling async like in rust?

Re: Zig's New Async I/O

#97
post #78

Earlier quoted context omitted.

Not necessarily, that is one of the reasons OOP exists. Have a struct representing the set of associated activities, owning the channel.

soo, a context?

You can take that view, yes.

But if you store your context in a struct (which is not the recommend “best practice” – but which you can do) it's no longer a function coloring issue.

I do that in on of my libraries and I feel that it's the right call (for that library).

Re: Zig's New Async I/O

#99
post #14
post #3

I feel that I have to point this out once again, because the article goes so far as to state that: > With this last improvement Zig has completely defeated function coloring. I disagree with this. Let's look at the 5 rules referenced in the famous "What color is your function?" article referenced here. > 1. Every function has a color Well, you don't have async/sync/red/blue anymore, but you now have IO and non-IO fun…

Go also suffers from this form of “subtle coloring”. If you’re working with goroutines, you would always pass in a context parameter to handle cancellation. Many library functions also require context, which poisons the rest of your functions. Technically, you don’t have to use context for a goroutine and could stub every dependency with context.Background, but that’s very discouraged.

Like you said you dont NEED context. Its just something thats available if you need it. I still think Go/Erlang has one of the best concurrency stories out there.

Re: Zig's New Async I/O

#100
post #63

Earlier quoted context omitted.

> Adding either one causes it to not be callable from certain places. you can call a function that requires an io parameter from a function that doesn't have one by passing in a global io instance? as a trivial example the fn main entrypoint in zig will never take an io parameter... how do you suppose you'd bootstrap the io parameter that you'd eventually need. this is unlike other languages where main might or might…

>you can call a function that requires an io parameter from a function that doesn't have one by passing in a global io instance? How will that work with code mixing different Io implementations? Say a library pulled in uses a global Io instance while the calling code is using another. I guess this can just be shot down with "don't do that" but it feels like a new kind of pitfall get get into.

while not really idiomatic, as long as you let the user define the Io instance (eg with some kind of init function), then it doesn't really matter how that value is accessed within the library itself.

that's why this isn't really the same as async "coloring"

Post reply on HN