Coroutines allow a function to suspend and
yield a result in the middle of the function.
This can be used for lazy generation of results.
Coroutines are hence similar to streams, where the state of the stream contains the position of the yield of the last value.
Applications of Coroutines
TBW: It is unclear to me what the main application of coroutines is, where do they provide a real benefit over streams?
Implementation of Coroutines
Any non-recursive coroutine can be mapped to a stream by performing these steps:
- turn all local fields of the coroutine into fields of the stream such that they keep there values for later calls.
- number all yield instructions
- add fields
last_resultto the stream, originally initialize
if last_yield = n last_yield := -1 else last_yield := n; result := v
nis the number of the yield instruction.
- For all code dominated by a yield, put that code into a condition
if last_yield < 0such that we drop out of the current function directly as soon as last_yield was set to a non-negative value.
For all code on the shortest path from the function entry to a yield
instruction, put that code into a condition
if last_yield < 0. For all conditionals on the path, add
last_yield = n ||before the condition if the if-branch leads to the yield, otherwise add
last_yield != n &&if the else-branch leads to the yield. Then, any call to that function will directly fall through to the code of the yield instruction and continue execution there.
- The last two steps could be simplified if the implementation could perform a goto from the yield instruction to the functions end and from function entry to the yield instruction.
TBD: Do recursive co-routines make any sense?