3 ms·
Aside from the demand for explicitness, we may not have to choose one or the other. The promise of Zig's semantics w.r.t. async is that you can write a library
by anonymoushn 5y ago
Aside from the demand for explicitness, we may not have to choose one or the other. The promise of Zig's semantics w.r.t. async is that you can write a library that accepts allocators, writers, and readers from the end user, and your library's functions will be specialized at compile time to be async or not-async based on what kind of writers and readers were passed in. If we had a broader effects/capabilities/whatever system, it would still be nice if library authors could address the needs of many different users without caring whether those users want to do IO.
If we really need to be explicit, we begin to need different libraries for each combination of effects/capabilities/whatever, sort of like how Rust users need at least 4 separate sync/async/nostd-sync/nostd-async libraries just to speak websockets. This seems like a waste to me.
Zig's error system in which errors must be handled and functions can opt to have their error sets inferred by the compiler (again, this includes specialization to different error sets for instances of the function that are parameterized by functions with different error sets) sort of gives us the same thing for errors that we have for async-ness, but I can sympathize with a perspective that feels that special casing these two things and not allowing any others is inelegant or whatever.
> Async is probably one of the less interesting constraints, but I find it so much more confusing when languages have colorless functions... I can't see suspension points, I can't tell wtf is going on. It's extremely frustrating.
Zig's view on this, which I endorse, is that suspending to your event loop is not substantially different from suspending to the kernel. Zig sort of assumes that we don't want to manually mark each function with which syscalls it and its callees are allowed to make, but maybe it's worthwhile to make a language that does do that.
- throwaway82652 5y ago>and your library's functions will be specialized at compile time to be async or not-async based on what kind of writers and readers were passed in Based on what I've seen in Rust's async, you don't actually want this in a low level language. Those calling scenarios are fundamentally different. If you magically make something async, other code can now suddenly mutate state that your function is referencing on its own stack and really mess things up. In Rust this isn't an issue because you're not allowed to pass references into an async function that outlive the future. If you take a synchronous function that holds references and add the async keyword, you probably will get compile errors about this. I don't think Zig has anything like that. >Zig's view on this, which I endorse, is that suspending to your event loop is not substantially different from suspending to the kernel. No, that doesn't make sense, not even in the context of Zig. Suspending to the event loop allows other coroutines to run. Blocking in a syscall stops the whole program and all the coroutines.
- anonymoushn 5y agoIt's insane to need to implement the websocket protocol 4+ times instead of 1 time.
- pcwalton 5y agoIn Rust, you can just implement the WebSocket protocol in async, and then users who want the synchronous version just use block_on.
- throwaway82652 5y agoI have bad news for you, there are probably thousands of HTTP implementations out there, thousands of TCP implementations, thousands of SMTP implementations, etc. That's what happens when a protocol becomes an international standard. But to address your comment directly, no it's not, not when you're actually building a different state machine with different constraints each time. I actually wish we could put this protocol code in one C library in a way that only handles the protocol logic and not the I/O part, and then we'd be done with it forever. But you know that wouldn't be good enough for some, those libraries exist and people still don't use them and instead they go and write their own in the new fancy language with their cool new concept for a state machine.
- anonymoushn 5y ago> I actually wish we could put this protocol code in one C library in a way that only handles the protocol logic and not the I/O part, and then we'd be done with it forever. Yes, this is a great approach. Unfortunately people in e.g. the Rust world tend to write crates that assume they can call malloc, recv, and send, rather than one crate just implementing the protocol logic and another that does all that other stuff. Zig's way of doing things causes the library people naively write and use to call malloc, recv, and send to also be a library that just implements the protocol logic, reads from and writes to static buffers, and doesn't call malloc. Edit to reply: Yes, Rust has no_std, but most libraries that implement a protocol are not divided into a no_std library that implements just the protocol logic and a separate std-using library that wraps it, nor are most libraries that implement a protocol asynchronously wrappers around a no_std library that is not async and only implements the protocol and makes 0 syscalls. A consequence of the fundamental non-composability of naively written Rust libraries is that users who want the wrong combination of asyncness and malloc-calling very frequently need to reimplement existing Rust libraries. A consequence of the fundamental composability of naively written Zig libraries is that they never need to do that.
- mananaysiempre 5y ago> The promise of Zig's semantics w.r.t. async is that you can write a library that accepts allocators, writers, and readers from the end user, and your library's functions will be specialized at compile time to be async or not-async based on what kind of writers and readers were passed in. If we had a broader effects/capabilities/whatever system, it would still be nice if library authors could address the needs of many different users without caring whether those users want to do IO. The problem is that the library author must then code for the worst case and the compiler won’t help them; injecting preemption into synchronous code can expose things about it that used to be unobservable, but now can make the difference between success and deadlock. (Similarly to how injecting mutation into pure code can expose—and bar the optimizer from changing—things that were previously unobservable, like “in which order does map() visit elements?”.) There still will be async-compatible and async-incompatible libraries, except now you can’t tell which one is which except by looking at the documentation—look at the lengths R5RS goes to in order to define things like returning multiple times from inside a letrec while R6RS just flat out prohibits that. Yes, this seems to be a pro-colouring comment while elsewhere in this discussion I make what seem to be anti-colouring comments. I really don’t know what the answer is.
- anonymoushn 5y agoI don't have much to add to this. These are real issues, and we have encountered and needed to fix them, and we probably will many more times. I would like to one day use tools that don't have these problems but also don't compromise on composability.