Coroutines are separate threads of execution that voluntarility transfer control to each other.
Instead of terminating like threads, the coroutines suspend themselves to relinquish execution to the other thread.

In some sense, A owns B. B yields to A, and can include ‘return values’ in its yield. A can ‘resume’ B at any point.
B can call other functions, and can create a lot of recursion. A consequence of this is that we can't have B and A living on the same stack frame. All this recursion could overwrite the coroutine's stack space when it's not actually running.
So, coroutines need to have their own separate stack space.

Coroutines can run concurrently to whoever called them, subroutines cannot.

Each nested stack fram stores a pointer to its caller's stack frame, so it can access variables in that scope.
-stupid fucking diagram. you get it but next time you look at it its not gonna make any sesne.
- remember the ‘cactus' doesnt mean anything without the scop diagram on the side. the reason S shows up on B's branch is because B is a coroutine that was called by M, and B called S, so S will appear on B's stack frame. B is allowed to call S because it is defined in the same scope as B, which is a direct child of M.
- scope: a function can see any of its children, anything defined in its parent, and the DIRECT children of any of its ancestors.

Index