This is outstanding. They solved the „what color is your function“ problem! The Rust ecosystem has a completely separate std-lib just for async. I think there is one other language that managed to avoid that problem too by using compile-time magic [0].
I mean, just because someone made a crate with std in the name doesn't make it another standard library. The actual standard library is still useful in async, as only the things that does IO or otherwise needs to sleep requires special handling.
I know of only one other, in erlang writing code to "just block" works as it should. In erlang processes are even fully preemptible too, not just at possibly-blocking points, although native code blocks the OS thread like in loom.
From what I understand, Erlang only preempts at function entry or exit, or at marked points in NIFs (C code).
Because functional programming requires loops to be written as recursive function calls, you can't do too much without calling/leaving a function, so a process can't avoid premption.
I don't understand, you can call a coroutine from a synchronous function and call a normal or suspend function from a suspending one.
By colorless we mean that you can use a suspend return type directly and not through the ugliness of a promise.
Kotlin did choose to have the suspend keyword but it's just an explicit type, kotlin could have had no suspend keyword and be officially colorless, it is technically colorless and is I believe the best of both worlds
> I don't understand, you can call a coroutine from a synchronous function and call a normal or suspend function from a suspending one.
Not without a syntactic distinction, and not without losing the performance characteristics you want coroutines to begin with.
> By colorless we mean that you can use a suspend return type directly and not through the ugliness of a promise.
That's not colourless. Colourless means that there is no syntactic distinction between sleep and delay. Two subroutines that mean the same but have a different syntactic colour.
> kotlin could have had no suspend keyword and be officially colorless,
It could not. You'd need to control the JDK to do that, or depend on class reloading, or pay a very high price in performance. Not to mention that it still wouldn't help when calling Java code (directly or indirectly) nor would it change how, say, JFR observes your program.
The problem is that Kotlin has very little influence over the platforms it targets -- Java, Android, JS and LLVM (well, maybe it has some influence over Android) -- and so must do its best to implement its functionality on top.
> I believe the best of both worlds
We don't want two worlds. We don't need two worlds. We want one that behaves as it should.
Green threads have been around for a very long time before Go, and there's nothing specific about Go that made them solve any problem they didn't solve before. It just so happens that this is a solution that comes with many downsides, which is why it's not universally adopted.
Indeed, Java itself had green threads (only!) in the first couple of versions.
Its downsides depend on the language. It works better in languages with GC than languages without, and it works better in languages that rarely rely on FFI than in languages that rely on it a lot. One of its major "downsides" is that it's just harder to implement than async/await, and requires control over the backend, something few languages have (even Rust is on top of LLVM). For example, Koltin just couldn't implement useful usermode threads because it has no control over the JDK.
Golang occupies an interesting spot here. They never had to migrate from a predominantly blocking, thread-based ecosystem to async. Does Golang really have two colors, is explicit threading a thing (I honestly don't know)? Or is it really just one color, namely the async one?
Golang doesn't really have a sync context. It has one color because everything is async. The `go` operation is not comparable to `await`, rather it is comparable to spawning.
There’s a difference between sync and async code in Go, which is why you have all the normal threading primitives like mutexes, semaphores and blocking queues/channels.
The point is that functions themselves don’t come in sync or async flavors. Just like in Java.
I don't think the existence of mutexes, semaphores and queues/channels imply that there is a sync version of Go. You can totally use those primitives in asynchronous Rust too.
You call the queues blocking, but they aren't really in the sense of "blocking" usually used when talking about async in Rust. The Go runtime can and will preempt your Go code in the middle of waiting for a channel to run some other task, and this preemption is what makes it different from a blocking Rust channel. An async Rust channel will also make the calling function wait for messages when you await the receive method.
Basically my point is that because any Go code can be preempted at any point, that makes all Go code async. The language not making you type await on everything doesn't make it sync.
Well, my point is that go has the same concept of sync and async as Java. All Java code can be preempted at any point, thats how threads work. Go is more efficient at scale as it uses a more light weight unit of concurrency under the hood, but from a developers standpoint the code functions in the same way.
So if you think Go is pure-async, then Java is pure-async, as it has access to the same primitives as Go for dealing with concurrency. It’s just that Java, at the moment, spawns a full thread wheres Go does something more light weight under the hood.
Unless, of course, you define async as doing something with coroutines/fibers. But I’d argue that is an implementation detail.
In any case. We are essentially agreeing. Go avoids the two-color problem by having single colored functions. Wheras Rust, JS, C# have two-colored functions.
Well I define async as being able to run many things without spawning a separate thread for each thing, by somehow swapping the current task every so often.
Call it an implementation detail if you want, but in my eyes, it is what makes the difference between all-async and all-sync.
[0] https://github.com/ziglang/zig/issues/1778