The new design makes the event loop / io much easier to reason about. Thanks Andy
Zig's New Async I/O
211–220 of 293 posts
Re: Zig's New Async I/O
#212I 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…
So this is a tangent from the main article, but this comment made me curious and I read the original "What color is Your Function" post. It was an interesting read, but I guess I came away confused about why "coloring" functions is a problem. Isn't "coloring" just another form of static typing? By giving the compiler (or interpreter) more meta data about your code, it can help you avoid mistakes. But instead of the u…
Async as a keyword doesn’t solve this or make writing parallel code any easier. You can still mess this up even if every function is annotated as async.
> A function with only synchronous code behaves very differently than one that runs code inside another thread or in a different tick of the event loop.
I think this is conflating properties of multiple runtimes. This is true in JavaScript because the runtime works on an event loop. In Java an “async” function that reads from a file or makes an http call doesn’t run in a different threads and doesn’t run in a different tick of an event loop. So what value does it have in that type of runtime?
Personally for me I think “async” is putting pain on a lot of developers where 99% of all code is not parallel and doesn’t share memory.
Re: Zig's New Async I/O
#213Earlier quoted context omitted.
But how will that actually work? Your stackless coroutines proposal talks about explicit primitives for defining a coroutine. But what about a function that's not designed for any particular implementation strategy - it just takes an Io and passes it on to some other functions? Will the compiler have a way to compile it as either sync or async, like apparently it did before? It would have to, if you want to avoid fun…
Right, the proposal doesn't discuss the implementation details -- I do apologise if that made it seem a little hand-wavey. I opted not to discuss them there, because they're similar-ish to the way we lowered stackless async in its stage1 implementation, and hence not massively interesting to discuss. The idea is that, yes, the compiler will infer whether or not a function is async (in the stackless async sense) based…
Is it though? I believe this would be an issue if you want to pass that function as a function pointer to an FFI function, in which case it must be sync.
Re: Zig's New Async I/O
#214Earlier quoted context omitted.
You are skipping the massive point here. If you are using a library in rust, it has to be async await, tokio, send+sync and all the other crap. Or if it is sync api then it is useless for async application. This approach of passing IO removes this problem and this is THE main problem. This way you don’t have to use procedural macros or other bs to implement multi versioning for the functions in your library, which do…
> If you are using a library in rust, it has to be async await, tokio, send+sync and all the other crap Send and sync is only required if you want to access something from multiple threads, which isn't required by async await (parallelism vs concurrency) 1) You can use async await without parallelism and 2) send and sync aren't a product of async/await in Rust, but generally memory safety, i.e. you need Send generall…
In theory this is correct. In practice, a lot of APIs (including many in tokio) require both traits even for single-thread use cases.
Re: Zig's New Async I/O
#215Earlier quoted context omitted.
I'll let a real category theorist get into the details that I'll likely flub, but the IO monad is where you end up if you start on this path. That context can be implicit, but it's there, and if you want any help from the compiler (to, for example, guide Claude Code towards useful outcomes) you've got to reify it as a real thing in the formality of the system. Async and coroutines are the graveyard of dreams for syst…
it's not a monad, since you can do unholy (for fp) things with it, like stash it in a struct and pass the struct around (even to functions which have no clue theres an io call) or just grab a globalized io object and use that at will arbitrarily at many entrypoints in your function. most importantly, besides the obvious situations (creating the io object, binding it to another object), it's not generally going to be…
It's not really any less natural than thinking stateful programming, except now the state is a reified thing, which I think is strictly advantageous once you get used to it.
Re: Zig's New Async I/O
#216Earlier quoted context omitted.
So this is a tangent from the main article, but this comment made me curious and I read the original "What color is Your Function" post. It was an interesting read, but I guess I came away confused about why "coloring" functions is a problem. Isn't "coloring" just another form of static typing? By giving the compiler (or interpreter) more meta data about your code, it can help you avoid mistakes. But instead of the u…
> It was an interesting read, but I guess I came away confused about why "coloring" functions is a problem. Isn't "coloring" just another form of static typing? It is. Function coloring is static typing. But people never ever agree on what to put in typing system. For example, Java's checked exceptions are a form of typing... and everyone hates them. Anyway it's always like that. Some people find async painful and sa…
I love checked exceptions. Checked errors are fantastic and I think most developers would agree they want errors to be in the type system, but Java as a language just hasn’t provided the language syntax to make them usable. They haven’t made it easy to “uncheck” when you can’t possibly handle an error. You have to write boilerplate:
Something s;
try {
s = something();
} catch (SomethingException e) {
throw new RuntimeException(e);
}
It sucks when you face that situation a lot. In Swift this is really simple: var s = try! something();
Java also hasn’t made them usable with lambdas even though both Scala [0] and Swift have shown it’s possible with a sufficiently strong type system: try {
someCall(s -> {
try {
another(s);
} catch (CheckedException ex) {
throw new UncheckedException(ex);
}
});
} catch (UncheckedException ex) {
// handle somehow
}
It sucks. I’m hopeful one day we’ll get something like try! or try? and better lambdas. Maybe once all resources stop being poured into Valhalla.[0] https://docs.scala-lang.org/scala3/reference/experimental/ca...
Re: Zig's New Async I/O
#217Re: Zig's New Async I/O
#218Earlier quoted context omitted.
it's not a monad, since you can do unholy (for fp) things with it, like stash it in a struct and pass the struct around (even to functions which have no clue theres an io call) or just grab a globalized io object and use that at will arbitrarily at many entrypoints in your function. most importantly, besides the obvious situations (creating the io object, binding it to another object), it's not generally going to be…
i'm relatively confident that andrew will happily break the language again (god love him for that) when it becomes clear that you really want that algebra. though i will say, for a systems language, it's probably better to invert the lift/unlift relationship, default to do-notation and explicitly unlift into pure functions. that's almost what const meant in C++ to begin with but it lost it's way.
1. the io situation is basically restructuring analgously to the allocator situation, which is at this point battle tested. there are currently no "monads wrapping statefulness" anywhere in zig.
2. It's not in zig's nature to build something because it it satisifies an fp idiom. the abstractions and resulting "thing that the hardware does" (at least in release builds) are generally more or less obvious. the levels of compiler reinterpretation to achieve functional purity are not really the sort of thing that zig does. for example, zig does not have a privileged "iterator" method that the compiler reinterprets in a way that unrolls blocks or lambdas into loops without crossing a frame boundary.
Re: Zig's New Async I/O
#219Earlier quoted context omitted.
Green threads have issues with C FFI mostly due to not being able to preempt execution, when the C thing is doing something that blocks. This is a problem when you have one global pool of threads that execute everything. To get around it you essentially need to set up a dedicated thread pool to handle those c calls. Which may be fine - go doesn't let the user directly create thread pools directly but do create one un…
The problem is when C calls expect to be on a particular thread. Either the main event thread, in a GUI context, or the same thread as some related previous call.
Re: Zig's New Async I/O
#220Does this mean that as a side effect, it'll now be possible to enforce functions are pure/deterministic in Zig by not passing in an Io?
Not quite: * Global variables still exist and can be stored to / loaded from by any code * Only convention stops a function from constructing its own `Io` * Only convention stops a function from reaching directly into low-level primitives (e.g. syscalls or libc FFI) However, in practice, we've found that such conventions tend to be fairly well-respected in most Zig code. I anticipate `Io` being no different. So, if y…