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.