DEV Community

Discussion on: Why I stopped using Coroutines in Kotlin

Collapse
 
martinhaeusler profile image
Martin Häusler

That's not a real argument. If it were, all of us would still code in C, or even assembler, because it is "more efficient". We're not talking about "minor inconveniences" here; each of the problems listed in the blog post are potential dealbreakers in their own right.

What kind of information do you want to convey with your "sorce"? Is this the modern version of "I'm a pro so you better believe it"? lol

Collapse
 
lukasz_bialy profile image
Łukasz Biały

Are you seriously comparing using C vs a jvm language with using sync I/O vs async I/O? Because for IO bound apps a java non-blocking app can easily be more efficient than a blocking app in whichever compiled to native language. Your arguments are minor inconveniences - it's not a deal breaking problem that you can't use step over in debugger. It's not a deal breaking problem that your threads are shifted under your code. It's just inconvenient to your habits and it's not only possible but relatively simple to learn to debug async code on the jvm with current tooling.

Source clause was meant to convey information that I'm not pulling my opinion out of a hat. Asynchronicity, thread shifting and non blocking I/O are something completely normal in Scala ecosystem.

Collapse
 
psfeng profile image
Pin-Sho Feng • Edited

+1 to the counter-arguments in this thread.

I think Kotlin's coroutines are an impressive piece of engineering but if there's something I regret it's that it encourages to write code in an imperative style. Note that that's not a problem with coroutines per-se, they're low-level powerful constructs that can be used to build higher-level libraries.

With imperative style it's only a matter of time plus sufficient amount of different hands and a few deadlines that the code becomes spaghetti. Then you add some state to it and it becomes a non-parallelizable mess.

I'd encourage you to try to avoid sharing state and actually pass the arguments you need. Even though it might seem annoying initially, it's going to help make your functions pure, with all the advantages of that (parallelization, for example). Also, there are patterns to mitigate the problem (e.g. the Reader monad in the functional world). Studying functional programming would help you see things in a different way.

Regarding locks, the documentation is clear that you should use Mutex, if you really have to. You could also try to use thread-safe data structures. Still, I believe that you could probably find a way to avoid sharing variables. Maybe look into actors?

I would have to agree with you on the debugger's issue though, but at least there's a workaround.

Collapse
 
skittishsloth profile image
Matthew Cory

Having to add a new breakpoint is a potential deal breaker? Sorry, in my debugging sessions I've got anywhere from 10-20 going at a time and I add and remove them as the need arises.

Having to pass state along as a parameter to functions (unless you can get the coroutine context going) is a practice I'd encourage anyways over thread local. I've had to rewrite stuff at work because it depended on ThreadLocal instead of a supplied state, and reactive code has the same "issues" as coroutines. We had issues of our own figuring out how to get a reactive context working with Flux/Mono code so we wrote our own stuff to deal with it. Took a day, give or take, and we can use that elsewhere.

The only point you've made that's a potential deal breaker is regarding locks and synchronized blocks. That's not something I considered and thank you for pointing it out. Roman commented on this thread; I'd love to hear his take on that because I've never read anything about whether they're needed or how they're supposed to be dealt with (if at all). Maybe you came across a legitimate bug.

That said, I've been fortunate enough that in almost 20 years of Java work I've yet to work with threads to such a level I really needed synchronization or locks. I've found it's far easier to create immutable objects in spite of the added boilerplate they require; then you don't have to worry about race conditions. I have yet to come across an instance where that's not possible, but I'm sure they're out there and when that comes up this would be good to know.

Lukasz' argument applies perfectly. It has nothing to do with efficincy. You're having problems dealing with a different way of handling something you're used to doing. Instead of approaching this as "here's some gotchas I found in coroutines and how to work around them" you came it as they're crap and you recommend avoiding them. And because you legitimately think an extra breakpoint is a problem I'm not inclined to take your opinion very highly on this.

You should respond to Roman. He's got a bit of insight on Kotlin and coroutines. His feedback might be useful.