Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Coroutines are a control-flow mechanism. They're a single-threaded pattern in as much as for loops are a single-threaded pattern. Ability to write multithreaded programs does not exclude the need for good single-threaded tools.


Looking at python's asyncio coroutine library, they are just mocking multithreading with asyncio.gather. Since coroutines can be executed in any order they are not really control-flow mechanisms. The selling point of coroutines over traditional threads is its lightweight but its moot since goroutines has similar memory cost to coroutines. The only real benefit is that coroutines are non blocking while goroutines may be blocked. There is no real benefit of having python's coroutine in go since goroutines does the same but better.


The concept of "coroutines" is as much about control flow as "subroutines" (function calls and return statements).

But when you have a construct that has their own call stacks, it's a relatively small step to implement lightweight threads with it.

Since doing concurrency happens much more often than any other smart use of coroutines, many people conflate the two.

I sometimes see this confusion in discussions about Kotlin's coroutines. An example of using coroutines that is not about concurrency: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.sequence...


Kotlin's sequence pre-dates co-routines. Mostly what either of those do is just a bit of syntactic sugar on top of call back mechanisms. One of the nice things with Kotlin is the ability to extend existing APIs via extension functions. Which is something the co-routines library uses extensively to be able to provide co-routine implementations on top of existing frameworks on the JVM, in javascript, and in native environments. Same APIs. Same code. Different underlying implementations. I actively use this in some of my multi platform libraries that I use on JVM and in the browser.

The key concept with Kotlin's co-routines is not having to choose between reactive, green threads, or real threads but treating all of those in the same way with a robust set of abstractions. I use co-routines in the browser on top of existing javascript frameworks that return promises. I use them on the JVM with reactive frameworks like flux. And I also use them with some thread pools. Once Loom reaches LTS (next year, I think), I'll probably be configuring some Loom capable co-routine dispatchers as well.

The debate regarding co-routines vs. go-routines seems like it is similar. I think Go can learn a thing or two from Kotlin's co-routines. After all it is mostly built as a library on top of a single language feature: the suspend keyword. I get that not everybody likes colored functions. But then having a lot of leaky abstractions and related complexity on top of go routines is maybe also not ideal.


> Kotlin's sequence pre-dates co-routines.

You misunderstood. The `Sequence` type does predate coroutines. But I meant the `sequence` builder function, which takes a block of suspending code to create a `Sequence`.

The in-order traversal in the article can be translated to Kotlin:

    fun walk(t: Tree?): Sequence<Int> = sequence {
        if (t != null) {
            yieldAll(walk(t.left))
            yield(t.value)
            yieldAll(walk(t.right))
        }
    }
As I have noted above, this is a use of coroutines that has nothing to do with concurrency.


Sequences actually were part of Kotlin 1.0. Co-routines were added later.


> Sequences actually were part of Kotlin 1.0.

But the `sequence` builder function was not. In fact it depends on coroutines.

Could you read my reply before repeating?


In fact python had general coroutines first in yield, which is mostly used for iteration (“generators”) and state machines.

Some frameworks (e.g. twisted) did use them for concurrency, and the core team originally planned something similar, however the ergonomics were not what they wanted (especially when mixing coroutines-for-concurrency and coroutines-for-iteration), so they went for a more specialised design.


> did use them for concurrency

> went for a more specialised design

My impression is that JS evolved similarly - (ab)using generator for concurrency, then specialized async-await as a language feature.


Kinda? But that was a very short cycle, both promises and generators were added to the language in ES6 (though the community had been coalescing around promises — “thenables” — for a while), the first draft for async functions was actually created during the development cycle of ES6, and it was shipped in ES7.


I don't know about Python, but this description of coroutines, the general concept, doesn't seem accurate to me. They most definitely cannot be executed in any order. They also have nothing to do with multithreading whatsoever. Lua has the most honest-to-god implementation of coroutines I know of, so I'd suggest looking at that. I've seen the word "coroutines" used in some weird way suggesting multithreading, which probably means Python used them to fake multithreading, but the idea originally has nothing to do with it. The idea actually started out as a way to better structure an assembly program in 1958: <http://melconway.com/Home/pdf/compiler.pdf>.


Goroutines are non-blocking. Channels may not always be, however.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: