Updated 2016-10-19 20:17:21 by u_Ltd

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"

Coloop  edit

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