Coroutines (Proposed) - Spicery/Nutmeg GitHub Wiki
Coroutines are functions that run to a 'yield' point and then return - but can be resumed for further results. These have been made familiar in mainstream programming through Python generators and C# iterators. The following proposal is largely based on the "process" model of Pop-11 because it avoids the limitations of generators.
Explanation by Example
Look and feel
Just to give the idea, a simple coroutine definition would look like the following:
@coroutine ### Required, even though the 'yield' implies it.
def factorials( n ):
var sofar := 1
for i in [1 ... n]:
yield( sofar ) ### Mandatory parentheses - similar to returns. Control returned to original caller but context saved.
sofar <- sofar * i
endfor
end
Typical use
Normally you will iterate over the coroutine results like this:
for i in factorials(3):
println( i ) ### Prints 1 then 2 then 6 in turn.
end
Basics
The way a coroutine definition works under the hood is that calling the factorial
function will create the coroutine object itself.
C := factorials(3)
Calling the coroutine object C
will then run the body of the code until it reaches a yield
statement. At that point, control propagates back to the call-site of the coroutine saving the state of all intermediate calls as it goes. The value returned to the calling code will be the value of sofar
. However, C
is not terminated and C
can be called again. Each time C
is called after that, execution continues from where it is left off and then yields the next value of sofar
.
println( C() ) ### Prints 1
println( C() ) ### Prints 2
println( C() ) ### Prints 6
After three results C
would naturally exit. This is signaled with a return option Stop()
. In addition the liveness of C
can be tested with the predicates 'IsLive'.
C() ### This will complain that `Stop` was uncaught.
IsLive( C ) ### prints false.