yield, a
built-in Tcl
command, exits the current coroutine without destroying it, returning a value in the process.
Synopsis edit
-
- yield ?value?
See Also edit
- yieldto
- instead of yielding a value directly, execute a command in place of the current coroutine context, which then returns a value.
- tcl::unsupported::yieldm
- an experiment in passing multiple values to a coroutine. yieldm turned out to be trivial to implement in terms of yieldto.
Description edit
yield arranges for the associated coroutine command of the current coroutine to return
value if it is provided, or the
empty string otherwise. The current coroutine context continues to exist. The next time the associated coroutine context command is executed, any argument passed to it becomes the return value of the
yield that previously exited the coroutine context. If the associated coroutine context command is not given an argument, the return value of
yield is the
empty string.
DKF: Can also be thought of as a magical form of
return. That itself returns (possibly).
Example: Basic edit
proc onetwo {} {
yield [info coroutine]
yield 1
return 2
}
% coroutine count onetwo
::count
% count
1
% count
2
% count
invalid command name "count"
DKF: If you're trying to build a coroutine that iteratively yields values, it can be a little bit tricky to work out how to string the value passing through
yield nicely; the values just don't seem to pass around in a way that feels natural (well, to me anyway). Here's a helper procedure that makes it all much easier:
proc coloop {var body} {
set val [info coroutine]
upvar 1 $var v
while 1 {
set v [yield $val]
set val [uplevel 1 $body]
}
}
With this, you get an automatic yield of the name of the coroutine to start with, and then you can pass values in and have results passed back. The given
variable is set to the value passed in, and the result of evaluating the
body is the next value yielded. You might use it like this:
proc summerBody n {
coloop x {
incr n $x
}
}
coroutine s summerBody 0; # ==> ::s
s 10; # ==> 10
s 5; # ==> 15
s 2; # ==> 17
s 1; # ==> 18
Without
coloop, you'd need to write something like this:
proc summerBody {n} {
set x [yield [info coroutine]]
while 1 {
set x [yield [incr n $x]]
}
}
Which is not much longer, but
is much less clear as to what value is flowing where. With longer bodies, it becomes even less clear.
Example: yield From Any Command in a Coroutine Context edit
yield can happen in anywhere in a coroutine context, not just in the
command specified as the initial entry point to the
coroutine context. In the following example,
alphadig is the initial entry point, but
p1 and
p2 also yield from the same coroutine context:
proc p1 {} {
foreach digit {1 2 3} {
yield $digit
}
p2
}
proc p2 {} {
foreach digit {4 5 6} {
yield $digit
}
}
proc alphadig {} {
yield [info coroutine]
p1
}
set name [coroutine c1 alphadig]
while 1 {
set res [c1]
if {[namespace which c1] eq $name } {
puts $res
} else {
#discard last result, which is the coroutine "falling off the end" rather
#than a real value
break
}
}
Output:1
2
3
4
5
6