Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Tcl 8.6 yield and coroutines

328 views
Skip to first unread message

Charlie Bursell

unread,
Aug 11, 2016, 3:05:39 AM8/11/16
to
I am trying to wrap my head around the yield command along with coroutines. I must be mis-understanding something.

When I attempt to run example from: http://wiki.tcl.tk/3449 below, I get:

yield can only be called in a coroutine
while executing
"yield $i" ...........

Same error if I run example in docs.
What am I not understanding here?



# Structural code (due to DKF)
namespace path tcl::unsupported
proc iota {start end} {
for {set i $start} {$i<=$end} {incr i} {yield $i}
return -code break
}
proc UNIQUE_NAME {} {return cor[incr ::COUNTER]}
proc makeIota {start end} {
set cmd [UNIQUE_NAME]
coroutine $cmd apply {args {yield;tailcall {*}$args}} \
iota $start $end
return $cmd
}

# Demonstrate usage (due to MS)
set iota_1_5 [makeIota 1 5]
puts *[info command $iota_1_5]*
puts ---
while 1 {
set x [$iota_1_5]
puts $x
}

puts ---
puts *[info command $iota_1_5]*


Alexandre Ferrieux

unread,
Aug 11, 2016, 4:31:35 AM8/11/16
to
I suspect some backslash-whitespace accident in the process of copy-pasting the code, ending up in calling [iota $start end] out of coroutine context (just before "return" in makeIota).

To be sure please report the whole $::errorInfo, or even better [info errorstack].

-Alex

Donal K. Fellows

unread,
Aug 11, 2016, 6:09:01 AM8/11/16
to
On 11/08/2016 08:05, Charlie Bursell wrote:
> What am I not understanding here?

There is a space after the backslash (which appears to be a cut-n-paste
error somehow; I've just fixed the wiki page). You can see that things
have gone wrong by disassembling the [makeIota] command:

> % tcl::unsupported::disassemble proc makeIota
> ByteCode 0x0x1008bfc10, refCt 1, epoch 17, interp 0x0x100829a10
(epoch 17)
> Source " \n set cmd [UNIQUE_NAME] \n coroutine $cmd apply"...
> Cmds 5, src 137, inst 42, litObjs 6, aux 0, stkDepth 5, code/src 0.00
> Proc 0x0x1008d2a10, refCt 1, args 2, compiled locals 3
> slot 0, scalar, arg, "start"
> slot 1, scalar, arg, "end"
> slot 2, scalar, "cmd"
> Commands 5:
> 1: pc 0-6, src 6-27 2: pc 0-3, src 15-25
> 3: pc 7-19, src 33-88 4: pc 20-28, src 102-118
> 5: pc 29-40, src 124-135
> Command 1: "set cmd [UNIQUE_NAME] "...
> Command 2: "UNIQUE_NAME"...
> (0) push1 0 # "UNIQUE_NAME"
> (2) invokeStk1 1
> (4) storeScalar1 %v2 # var "cmd"
> (6) pop
> Command 3: "coroutine $cmd apply {args {yield;tailcall {*}$args}} \"...
> (7) push1 1 # "coroutine"
> (9) loadScalar1 %v2 # var "cmd"
> (11) push1 2 # "apply"
> (13) push1 3 # "args {yield;tailcall {*}$args}"
> (15) push1 4 # " "
> (17) invokeStk1 5
> (19) pop
> Command 4: "iota $start $end "...
> (20) push1 5 # "iota"
> (22) loadScalar1 %v0 # var "start"
> (24) loadScalar1 %v1 # var "end"
> (26) invokeStk1 3
> (28) pop
> Command 5: "return $cmd "...
> (29) startCommand +12 1 # next cmd at pc 41, 1 cmds start here
> (38) loadScalar1 %v2 # var "cmd"
> (40) done
> (41) done

If you look at Command 3's implementation, you see a pushing of a space
as an argument before the invokeStk, followed by Command 4 which is a
direct call of [iota]. That's not right at all! Fixing the trivial
whitespace issue will instead give this disassembly:

> % tcl::unsupported::disassemble proc makeIota
> ByteCode 0x0x1008bfc10, refCt 1, epoch 17, interp 0x0x100829a10
(epoch 17)
> Source " \n set cmd [UNIQUE_NAME] \n coroutine $cmd apply"...
> Cmds 4, src 123, inst 37, litObjs 5, aux 0, stkDepth 7, code/src 0.00
> Proc 0x0x102048490, refCt 1, args 2, compiled locals 3
> slot 0, scalar, arg, "start"
> slot 1, scalar, arg, "end"
> slot 2, scalar, "cmd"
> Commands 4:
> 1: pc 0-6, src 6-27 2: pc 0-3, src 15-25
> 3: pc 7-23, src 33-104 4: pc 24-35, src 110-121
> Command 1: "set cmd [UNIQUE_NAME] "...
> Command 2: "UNIQUE_NAME"...
> (0) push1 0 # "UNIQUE_NAME"
> (2) invokeStk1 1
> (4) storeScalar1 %v2 # var "cmd"
> (6) pop
> Command 3: "coroutine $cmd apply {args {yield;tailcall {*}$args}} "...
> (7) push1 1 # "coroutine"
> (9) loadScalar1 %v2 # var "cmd"
> (11) push1 2 # "apply"
> (13) push1 3 # "args {yield;tailcall {*}$args}"
> (15) push1 4 # "iota"
> (17) loadScalar1 %v0 # var "start"
> (19) loadScalar1 %v1 # var "end"
> (21) invokeStk1 7
> (23) pop
> Command 4: "return $cmd "...
> (24) startCommand +12 1 # next cmd at pc 36, 1 cmds start here
> (33) loadScalar1 %v2 # var "cmd"
> (35) done
> (36) done

See how the "iota" and the contents of the variables are passed into the
call to [coroutine]? That's looking like what we expect, and works
correctly too.

Donal.
--
Donal Fellows — Tcl user, Tcl maintainer, TIP editor.

Charlie Bursell

unread,
Aug 11, 2016, 9:36:10 AM8/11/16
to
Thanks for the clarification. I'll give it another shot
0 new messages