PYK 2017-08-23
The inter-coroutine return protocol allows participating coroutines to act as a virtual call stack where one coroutine "returns" to another coroutine by treating that coroutine as if actually was return.
In a normal call stack, a callee is tightly bound to its caller. When it returns, control returns to the caller. With coroutines this relationship can become more dynamic. A called coroutine can "return" to its "caller", but it could also "return" to some other "caller". In this way, the "call stack" can be composed dynamically. Each participating coroutine uses some coordinated mechanism to decide whether to "return" to its caller or to some other coroutine. This logical "call stack" becomes more like a production line, where output can be rerouted at any moment according to the decisions of some controller.
Because standard return information is being passed around, the Tcl interpreter can handle errors and other return codes in the normal manner. Condition handlers can be dropped into the workflow at any point, and can even "restart" some routine, passing it adjusted data.
The calling coroutine does this:
set result [return -level 0 {*}[yieldto some other command some arguments [info coroutine]]]
Or to process the return options in some custom fashion:
catch {return -level 0 {*}[yieldto some other command some arguments [info coroutine]]} cres copts process $copts and behave accordingly ...
The returning coroutine treats the the other coroutine as if it was actually return:
set args [yieldto the calling coroutine ${some result} set caller [lindex $args end] set args [lrange $args 0 end-1]
Any of the standard arguments to return can be used:
set args [yieldto the calling coroutine -code break]
This allows coroutines to interact in an intuitive and customary way. For example, a coroutine might iteratively call another coroutine which returns a series of results and then returns a break code:
while 1 { lappend res [call some other coroutine] } puts $res
For its part, some other coroutine would do something like this:
while {something is true} { reply ${another value} } yieldto the client -code break
In the last two examples, call and reply are helper commands that eliminate the need to type out the verbose patterns introduced above. In order to do its job, reply stores the name of the caller in the coroutine's top level. These and other helpful commands are available in ycl coro call.