| > A function has a single exit entry point and a single exit point and no state. If you call a function, you run its body. > A coroutine has several entry points and exit points, and an internal state. If you call a coroutine, you make an instanciation (the body doesn't run). Sort of, in some languages, but it doesn't have to work that way. First, let's note that "blues" do have state, and they put it on the stack. So blues can only be entered once and store state on the stack. Reds store state in an object, and they can yield out of the middle and be reentered. Then let's note that our top-level code has to be "red", or it would be impossible to ever run "red" code. Now let's go on a journey. 1. Make it so calling a red from a red will start running the body right away, by default. 2. When calling a red from a red and running the body, we're already inside a coroutine instance. Instead of making a new one, keep using the same one. Grow it and store the new state at the end. 3. When calling a blue, if we're inside a coroutine instance, put the blue's stack inside it. And since our top-level code is red, the we always are inside a coroutine instance. 4. Since all our stack frames are safely stored inside coroutine instances, it's safe to call from a blue into a red! The entire stack can be saved for later, blue and red alike. 5. At this point the only difference between blue and red is that a blue function cannot yield, it can only have something deeper on the stack yield. For fun, you could make 'yield' into a runtime-provided function. Now functions written in the language are all the same. There is no difference between blue and red. - So there is still a distinction between "coroutines" and "functions". But in this setup, a coroutine is merely a container that you run functions inside of. There is only one kind of code you run, and it is a function. Some language work this way. I wish javascript worked this way. |
It cannot be applied to legacy languages.
Just like you could not add a borrow checker to C without creating 2 worlds in the C community. But you can design Rust with this in mind.
A design always has a context.
Also, make a giant coroutine has a performance price, because any line is a potiential context switching.