Coroutines basically provide a means for a function to
sequence of results in the middle of the function, instead of just one result at
the end. This can be used for lazy generation of results.
Coroutines are hence somewhat similar to streams, where the state of the stream includes the position of the yield of the last value in addition to the state of all fields of the stream instance.
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:
- make all local fields of the coroutine fields of the stream such that they keep there values for later calls.
- number all yield instructions
- add fields
lastResultto the stream, originally initialize
if lastYield == n lastYield = -1; else lastYield = n; result = v
nis the number of the yield instruction.
- For all code dominated by a yield, put that code into a condition
if lastYield < 0such that we drop out of the current function directly as soon as lastYield 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 lastYield < 0, for all conditionals on the path, add
lastYield == n ||before the condition if the if-branch leads to the yield, otherwise add
lastYield != 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, of course, be simplified a lot 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?