coroutine(3tcl)
okay so bestie, you opened coroutine(3tcl) โ that's the Tcl Built-In Commands page for the whole coroutine family โ and honestly? it's giving iconic. sit with me, we're going through every single bit of it ๐
๐ NAME ๐
coroutine, yield, yieldto, coroinject, coroprobe โ create and produce values from coroutines โจ
๐ SYNOPSIS ๐
coroutine name command ?arg...?
yield ?value?
yieldto command ?arg...?
name ?value...?
coroinject coroName command ?arg...?
coroprobe coroName command ?arg...?
tiny page-nerd note, no cap: on the original page those last two lines (coroinject,coroprobe) carry a little2marker off to the side, and a whole bunch of the paragraphs below do too. that's the page's own marker, not something you type into your terminal. just so you're not out here confused ๐
๐ธ DESCRIPTION ๐ธ
The coroutine command creates a brand-new coroutine context (with an associated command) named name, and then it executes that context by calling command, passing in the other remaining arguments without further interpretation โ like, no extra parsing, no vibe check, it just hands them straight over. And once command returns normally or with an exception (e.g., an error), the coroutine context name is deleted. gone. poof ๐จ
Inside that context, values can be generated as results by using the yield command; if you don't supply a value, the empty string is used โ no value, no problem ๐. When yield gets called, the context suspends its execution and the coroutine command returns the argument to yield. After that you can resume the context's execution by calling the context command, optionally passing in the single value to use as the result of the yield call that caused the context to be suspended. And if the coroutine context never yields at all and instead returns conventionally? then the result of the coroutine command will be the result of the evaluation of the context. slay โจ
Your coroutine can also suspend its execution by using the yieldto command ๐ฆ, which โ instead of returning โ cedes execution to some command called command (resolved in the context of the coroutine) and to which any number of arguments may be passed. Since every coroutine has a context command, yieldto can be used to transfer control directly from one coroutine to another (this is only advisable if the two coroutines are expecting this to happen, iykyk) but honestly any command may be the target. If a coroutine gets suspended by this mechanism, the coroutine processing can be resumed by calling the context command, optionally passing in an arbitrary number of arguments. The return value of the yieldto call will be the list of arguments passed to the context command โ and it is up to the caller to decide what to do with those values. your call, bestie ๐
The recommended way of writing a version of yield that allows resumption with multiple arguments is by using yieldto and the return command, like this:
proc yieldMultiple {value} {
tailcall yieldto string cat $value
}
The coroutine can also be deleted by destroying the command name, and the name of the current coroutine can be retrieved by using info coroutine ๐. Also, heads up: if there are deletion traces on variables in the coroutine's implementation, they will fire at the point when the coroutine is explicitly deleted (or, naturally, if the command returns conventionally).
At the point when command is called, the current namespace will be the global namespace and there will be no stack frames above it (in the sense of upvar and uplevel). However โ plot twist โ which command to call will be determined in the namespace that the coroutine command was called from. the way that works is genuinely so unhinged and I love it ๐ซถ
๐ coroprobe, the little peekaboo ๐
A suspended coroutine (i.e., one that has yielded or yieldto-d) may have its state inspected (or modified) at that point by using coroprobe to run a command at the point where the coroutine is at ๐.
The command takes the name of the coroutine to run the command in, coroName, and the name of a command (and any arguments it requires) to immediately run at that point. The result of that command is the result of the coroprobe command, and the gross state of the coroutine remains the same afterwards (i.e., the coroutine is still expecting the results of a yield or yieldto as before) โ though variables may have been changed. sneaky, but legal ๐
๐ง coroinject, the plot device ๐ง
Similarly, the coroinject command may be used to place a command to be run inside a suspended coroutine (when it is resumed) to process arguments, with quite a bit of similarity to coroprobe. However, with coroinject there are several key differences:
- The coroutine is not immediately resumed after the injection has been done. A consequence of this is that multiple injections may be done before the coroutine is resumed. The injected commands are performed in reverse order of definition (that is, they are internally stored on a stack).
- An additional two arguments are appended to the list of arguments to be run (that is, the command and its args are extended by two elements). The first is the name of the command that suspended the coroutine (
yieldoryieldto), and the second is the argument (or list of arguments, in the case ofyieldto) that is the current resumption value. - The result of the injected command is used as the result of the
yieldoryieldtothat caused the coroutine to become suspended. Where there are multiple injected commands, the result of one becomes the resumption value processed by the next.
The injection is a one-off. It is not retained once it has been executed. It may yield or yieldto as part of its execution. no refunds, no reruns ๐
โ ๏ธ important warning, bestie, for real for real: running coroutines may be neither probed nor injected; the operations may only be applied to coroutines that are suspended. (If a coroutine is running then any introspection code would be merely inspecting the state of where it is currently running;coroinject/coroprobeare unnecessary in that case.)
โจ EXAMPLES โจ
okay bestie, this is the part where it gets fun ๐ every one of these is a little runnable vibe, so let's go through them one at a time, no skipping.
example one: the coroutine that never runs out ๐
This example shows a coroutine that will produce an infinite sequence of even values, and a loop that consumes the first ten of them. She's immortal but we're only taking ten, we're being so polite about it. ๐
proc allNumbers {} {
yield
set i 0
while 1 {
yield $i
incr i 2
}
}
coroutine nextNumber allNumbers
for {set i 0} {$i < 10} {incr i} {
puts "received [nextNumber]"
}
rename nextNumber {}
and yeah, the whole thing closes out with rename nextNumber {} ๐
example two: the accumulator ๐งฎ
In this example, the coroutine acts to add up the arguments passed to it. Every value you hand her gets folded into the running total and handed right back, it's giving very attentive friend energy fr fr.
coroutine accumulator apply {{} {
set x 0
while 1 {
incr x [yield $x]
}
}}
for {set i 0} {$i < 10} {incr i} {
puts "$i -> [accumulator $i]"
}
example three: the Sieve of Eratosthenes ๐ฅ
This example demonstrates the use of coroutines to implement the classic Sieve of Eratosthenes algorithm for finding prime numbers. And bestie โ note the creation of coroutines inside a coroutine. coroutine-ception. she contains multitudes. ๐ฆ
proc filterByFactor {source n} {
yield [info coroutine]
while 1 {
set x [$source]
if {$x % $n} {
yield $x
}
}
}
coroutine allNumbers apply {{} {while 1 {yield [incr x]}}}
coroutine eratosthenes apply {c {
yield
while 1 {
set n [$c]
yield $n
set c [coroutine prime$n filterByFactor $c $n]
}
}} allNumbers
for {set i 1} {$i <= 20} {incr i} {
puts "prime#$i = [eratosthenes]"
}
example four: the juggler trio ๐คน
This example shows how a value can be passed around a group of three coroutines that yield to each other. literally a group chat where the message keeps getting shorter. Moe, Larry and Curly said "we'll pass it around" and honestly? they ate. ๐
proc juggler {name target {value ""}} {
if {$value eq ""} {
set value [yield [info coroutine]]
}
while {$value ne ""} {
puts "$name : $value"
set value [string range $value 0 end-1]
lassign [yieldto $target $value] value
}
}
coroutine j1 juggler Larry [
coroutine j2 juggler Curly [
coroutine j3 juggler Moe j1]] "Nyuck!Nyuck!Nyuck!"
example five: the collector, plus we go snooping ๐
This example shows a simple coroutine that collects non-empty values and returns a list of them when not given an argument. It also shows how we can look inside the coroutine to find out what it is doing, and how we can modify the input on a one-off basis. peek inside her brain bestie, she won't mind ๐ซถ
proc collectorImpl {} {
set me [info coroutine]
set accumulator {}
for {set val [yield $me]} {$val ne ""} {set val [yield]} {
lappend accumulator $val
}
return $accumulator
}
coroutine collect collectorImpl
collect 123
collect "abc def"
collect 456
puts [coroprobe collect set accumulator]
# ==> 123 {abc def} 456
collect "pqr"
coroinject collect apply {{type value} {
puts "Received '$value' at a $type in [info coroutine]"
return [string toupper $value]
}}
collect rst
# ==> Received 'rst' at a yield in ::collect
collect xyz
puts [collect]
# ==> 123 {abc def} 456 pqr RST xyz
๐ฌ DETAILED SEMANTICS ๐ฌ
This example demonstrates that coroutines start from the global namespace, and that command resolution happens before the coroutine stack is created. yes, before. that ordering matters, iykyk. โจ
proc report {where level} {
# Where was the caller called from?
set ns [uplevel 2 {namespace current}]
yield "made $where $level context=$ns name=[info coroutine]"
}
proc example {} {
report outer [info level]
}
namespace eval demo {
proc example {} {
report inner [info level]
}
proc makeExample {} {
puts "making from [info level]"
puts [coroutine coroEg example]
}
makeExample
}
Which produces the output below. In particular, we can see that stack manipulation has occurred (comparing the levels from the first and second line) and that the parent level in the coroutine is the global namespace. We can also see that coroutine names are local to the current namespace if not qualified, and that coroutines may yield at depth (e.g., in called procedures). ๐ญ she's deep in the stack and still yielding, icon behavior.
making from 2
made inner 1 context=:: name=::demo::coroEg
๐ SEE ALSO ๐
๐ KEYWORDS ๐
coroutinegenerator
and that's the whole page bestie ๐ฅน๐ this one comes to you straight outta Tcl 8.6, filed under coroutine(3tcl) โ go forth and yield, you've got this ๐ฆโจ๐ธ
๐ฆ where this page comes from
- ubuntu questing: tcl9.0-doc 9.0.2+dfsg-1
- ubuntu resolute: tcl9.0-doc 9.0.3+dfsg-1
- ubuntu stonking: tcl9.0-doc 9.0.4+dfsg-1
- Copyright (c) 2009 Donal K. Fellows
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms