Hello everyone, I'm new here and just finished reading about the platforms in the documentation website
It seems roc platforms would be a good fit for asynchronous io
Does one exist? Or is there a proposal for one?
welcome! The short answer is that platforms are already able to do async I/O, but implementing it with good performance is very advanced and we don't have a good example of doing it end to end yet :smile:
@Richard Feldman we could have some implementation in roc, for example an event loop implemented in roc directly?
@souf that sounds backwards, I imagined the platform implements the event loop but only exports functions like gather, race, etc
@Raz why would it be backwards?
Having the platform doing it would require each platform to re-implement it, and potentially more complex types to make it ergonomic and compatible with a wide range of async operations/erros, etc... between the platform and roc. Having roc doing it, it would probably be simpler, and you gain compatibility with all platforms by doing it once and less platform specifics.
What I think is that the platform could expose something like that:
## Spawns a function returning `a` future that can be awaited
run_async! : (() => a) => Future a
## Waits for at least one of the given futures completes.
## returns the future that completed and its value.
wait_any! : List (Future a) => (Future a, a)
wait_any rather than just wait otherwise you would not be able to implement race efficiently.
From here, race, all, etc... is just pure logic around it.
Not sure what it involves as I haven't worked with platform yet thought, but I'm very interested in having such a feature.
Actually with this approach there is a loop in Roc, and potentially one in the underlying platform calls (that does the optimized waiting). So I'm not even sure how to call this. Is the roc part supposed to be called an "event loop"?
I asked Claude to do some exploration and give a summary based on old discussions here in zulip... I'm not sure this is super accurate as it's been a while since we've talked about this or done any experiments but it mostly looks ok to me https://gist.github.com/lukewilliamboswell/304c57045fa0116c1a94a1baddcaf469
yeah I'd say the "Mechanism 1" section is the most relevant - that prototype it links to is how we can have a Go-style experience of "I/O is automatically nonblocking without needing to write async or await"
I think it's better overall than actual async/await both because it gets rid of the ceremony of writing "async" and "await" all over the place (while still getting you the concurrency benefits) but also because you get real stack traces without having to pay an insane up-front performance cost
it's already possible today to implement a host that does this, but it's a lot of work :smile:
I want to work on that sometime after the 0.1.0 launch (which is on track to be later this year)
because separately from the host stuff to get the nonblocking I/O, we do actually need some compiler changes to be able to do the equivalent of a type-safe Promise.all from JS
Richard Feldman said:
you get real stack traces without having to pay an insane up-front performance cost
why would the performance cost be so big?
so I want to do that work together with the host work and have concurrency be a major theme of the 0.2.0 release
souf said:
Richard Feldman said:
you get real stack traces without having to pay an insane up-front performance cost
why would the performance cost be so big?
without going too deep down the coroutines rabbit hole:
so I think for us, the "stackful" approach is better.
separately, there's the question of how to hook into hosts that are using async, such as JS in the browser where effects like http requests are unavoidably async even if that's not what we're compiling directly to
in general, it's possible for us to do this stackful coroutines approach and then hook into the host's async machinery. On non-wasm targets, this can theoretically be pretty fast, although we'd need a working implementation to properly verify that.
On wasm, it's currently possible but slow because wasm's stack switching proposal (which was in Phase 2 last I checked) hadn't landed yet https://effect-handlers.org/talks/stack-switching-wasmcg-feb2025.pdf
Thanks for the nice explanation! Is that related to the fact that rust requires to pass a cli flag explicitly to enable stack traces?
I don't think so, actually
I think that applies to synchronous code, and I suspect it's more about whether there's a signal handler set up that collects backtraces on stack overflows (and other OS signals, like segfaults) as opposed to just doing the default thing of exiting (which might be what you want, in case for example your program is setting up its own signal handling and you don't want Rust to inject its own signal handling that could interfere with that)
Richard Feldman said:
in general, it's possible for us to do this stackful coroutines approach and then hook into the host's async machinery. On non-wasm targets, this can theoretically be pretty fast, although we'd need a working implementation to properly verify that.
On wasm, it's currently possible but slow because wasm's stack switching proposal (which was in Phase 2 last I checked) hadn't landed yet https://effect-handlers.org/talks/stack-switching-wasmcg-feb2025.pdf
WASI 0.3 is all about the Component Model's async ABI
https://bytecodealliance.org/articles/WASI-0.3
https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#concurrency
So it seems to made quite some progress.
oh I'm talking about wasm in the browser, not wasi :smile:
and the stack switching thing is what we really need, not async ABI
nothing wrong with wasi or anything, just that it doesn't help with the browser use case
ah right, of course
Is there a written design proposal for async platforms I can read through?
Or is it too early?
@Raz I think everything that exists is in the summary sent by Luke: https://gist.github.com/lukewilliamboswell/304c57045fa0116c1a94a1baddcaf469
Last updated: Jul 23 2026 at 13:15 UTC