Hacker News new | ask | show | jobs
by ragnese 1900 days ago
> Contrary to what you say it is the best language I've ever used for concurrency. It has it all, structured concurrency, cancelation, Flow, transparency (no await), etc.

I disagree.

Have you ever tried to actually implement something non-trivial that takes advantage of structured concurrency with cancellation? It's pretty hard to do correctly. Can you really tell me off the top of your head what the difference is between `withContext(coroutineContext) {}` and `coroutineScope {}` from within a suspend function?

Coroutines use unchecked exceptions for control flow. Kotlin also uses unchecked exceptions for fatal and non-fatal error handling. Figuring out how all these things interplay when it comes to coroutines and suspend functions has some subtleties that, IMO, are very difficult to figure out from just documentation and blog posts.

Also, Kotlin's standard types are entirely unsafe to use concurrently. The fact that MutableList inherits from List means that a function that accepts a List parameter CANNOT assume that the list wont change while the function is executing. So if you write `if (list.isNotEmpty()) { doSomething(list.first()) }` - that's a race condition because the list can literally become empty between the if clause and the body.

"But, wait! You should have just been smart enough to make a copy of your List before sending it between threads/coroutines." Okay, great. Let's do full copies of potentially-large collections. Thank goodness Kotlin is so concurrency ready that the standard collection types are persistent .. colle..ctions... oh.

Kotlin's concurrency story is really not that awesome. Scala is better, but still not perfect. Clojure is better still. Rust is good. Elixir (or anything with some kind of actor framework, I guess) is good. Haskell is good.

But I agree, overall, that if I had to pick a best app language today, it's either Kotlin or Scala, or Swift if you're writing for Apple stuff. I'll admit that I have a glaring experience gap with .NET languages, so I can't honestly say anything about C# and F#.

2 comments

> I'll admit that I have a glaring experience gap with .NET languages, so I can't honestly say anything about C# and F#.

C#/.NET comes with language-level mutexes (`lock`) and the .NET library has thread-safe generic collections (ConcurrentDictionary, ConcurrentBag) and true immutable collections (ImmutableArray, ImmutableList, ImmutableDictionary) with optimized copy operations (e.g. ImmutableList.Add is O(1), but ImmutableArray.Add is O(n)). It's a nice addition to the library with only a few warts.

That's good to hear.

Java/Kotlin also have mutexes- just not as language built-ins (well, it does have `synchronized`).

They also have a ConcurrentFoo set of collections as well. And actually an ImmutableMap (but I don't see ImmutableList, etc. Why?).

The "problem" is that they're opt-in.

I spent years writing multi-threaded C++. But I've become very spoiled with modern languages that make concurrency safe(r)-by-default, such as Rust, Clojure, and Elixir (I haven't actually used concurrent Haskell).

Honestly, it's not anywhere near as bad as it used to be. The ecosystem(s) have embraced immutability everywhere that it's possible, so you don't have to worry quite as much about accidents.

> but I don't see ImmutableList, etc. Why

.NET doesn't have an ImmutableList either (the ImmutableBag type is an unordered collection). This is because unlike with hash-tables you always need to lock the entire structure when mutating a List/Vector (with hashtables you only need to lock the specific bin/bucket).

Ah! Good point.
> Coroutines use unchecked exceptions for control flow

That's... horrible.

Why can't Kotlin do it the way C# does with heap-allocated state-machines?

Honestly, I truly don't know and I don't feel qualified to judge the merits of the two approaches. There could be performance or behavior implications that they just prioritized differently from how C# does it. Or maybe something to do with the Java underpinnings. Are C# promises/whatever cancellable?

But, as an "end user", the exception thing drives me absolutely nuts. It wouldn't be nearly as bad if Kotlin either didn't use exceptions for ALL error handling, or even if it had checked exceptions so that "normal" errors would be, mentally, separate from fatal errors and/or coroutine cancellation.

> Are C# promises/whatever cancellable?

Yes, but it's opt-in (it requires the author of the async method to add a `CancellationToken` parameter and to respect `IsCancellationRequested`).