Hi, I recently wrote my first program based on channels, and blew my mind how easy it was make sure the program runs on multiple threads, scales where it needs and generally, how easy it is to abstract a process with steps into channels.
I get why Go got so popular :)
So, what about Roc? To make it work, you need two things: a channel implementation and an easy way to run function on a new thread.
Here is how it looks in F#:
open System
open System.Threading.Channels
open System.Threading.Tasks
let channel =
Channel.CreateUnbounded<string>()
let writeTask =
task {
for _ in [ 0..10 ] do
do! channel.Writer.WriteAsync "message"
channel.Writer.Complete()
}
let readTask =
task {
while true do
let! message = channel.Reader.ReadAsync()
Console.WriteLine message
}
Task.WhenAll(writeTask, readTask)
|> Async.AwaitTask
|> Async.RunSynchronously
The only problem with this, is that you need a loop and channel.Reader.ReadAsync throws exception when channel.Writer.Complete is called.
I imagine that instead of a loop, you could use a recursive function and pattern match on a message from a channel. There could be a tag representing it beeing closed. Probably some backapassing would be nice here, since waiting for a message should be async. The same goes with writing to a bounded channel (with limited capacity), to manage backpressure.
What do you think? How could syntax for just firing up a fucntion on new thread look like? Does it need to be a full OS thread?
I love go and its threading+channels as well.
So this gets into a really interesting complication. Roc is compiled into another app. As such, Roc itself can not really have the concept of threads. Not that it is totally impossible, but that it will most likely have to be orchestrated by the specific platform.
A few examples of some more specifics:
With that all being said, a ton of more stand desktop or server apps would totally be reasonable to support great threading and channels.
So where does that leave us. I think that the best option long term would be to push for standardization around a channel/threading api that platforms can support. This would be in userland/a libary. A platform would then support the api and enable consistent use in Roc apps.
On another note, I am currently looking into async rust + roc. I think that it will mostly hide the async nature away from roc. It will just enable Roc to call host functions that happen to be async. Also will enable roc to spawn a lambda that is blocking (ending the current async thread). It will not allow any roc controlled multithreading.
But also it is super duper explicit and alpha right now. Just starting to test.
Well, even in C#/F# async actually desugars to callbacks on continuation (with some state machine inbetween). Channel implementation itself does not need multiple threads, just needs async - channel itself could be in standard library.
You only need multithreading for running the workers, but on microcontrollers that could be not available. Channels with single workers are also very nice to have, especially with nice syntax around them.
I'm not that deep in Roc to know whether I make sense :D I come from dotnet where the standard library is what made is so powerful and easy to use
Gleam is currently trailblazing a lot of ideas related to concurrency in a statically-typed (mostly) pure language.
just needs async
Roc could theoretically have it's own async, but it would be limited to current thread state machine based async. That being said, I think that more likely Roc will want to build on top of whatever async features the host has. As such, it may never have it's own form of async. Instead when building on async rust, it would use that. Which of course can't be part of the standard library because Roc doesn't just support rust as a host langauge. The host language can be anything with CFFI.
Again, not trying to say this can't happen or be negative. Just trying to show the relation between app and host. It leads to some interesting trade offs and often requires the host to create certain features for Roc. Roc is essentially sandboxed.
I guess to make Roc code more portable, the hope long term would be that a single userland library would become the standard for how to do async/multithreading/channels/etc in Roc. That library would define the nice Roc api and the primitives it requires. Then to be used with any platform, either the platform author or the end user just needs to write a tranlation function that goes from the userland library to the low level platform api. That should be possible and enable a more consistent store around these kinds of features. Of course, some platforms will not be capable of supporting the userland library due to not exporting the required low level features.
Last updated: Jun 16 2026 at 16:19 UTC