Version 6 of multi-arg coroutines

Updated 2010-04-15 04:09:49 by CMcC

I wish to consider amending "TIP 328 Coroutines" http://www.tcl.tk/cgi-bin/tct/tip/328.html , along these lines:

Invocation of a coroutine should accept multiple arguments, those arguments should be returned to the coroutine's ::yield as a list of actual parameters.

The current implementation forbids invoking a coroutine with more than one argument. I have found, in practice, that many of my coroutine invocations are naturally like other command invocations, and take multiple arguments.

The case for providing multi-arg'd coroutines is:

  1. coroutines should be able to emulate any command, not just any single-arg'd command [generality]
  2. to implement single-arg's coroutines in multi-arg'd coroutines is simple. The converse is inefficient and difficult. [increase in expressive power]
  3. there is no sound reason that the invocation of a coroutine should not resemble that of any other command [principle of minimal surprise]

For this reason, the core should be modified to accept multiple actual parameters to a coroutine invocation, ::yield should be modified to resemble ::yield2 below, and a new interface ::yieldm should be created to return the actual parameter list as it's passed.

-- Demonstration of point 2 --

CASE 1: (counterfactual)

Assume a Coroutine which generates a command taking multiple args, to implement coroutine as we have it implemented:

proc coroutine {name command args} {
    set ns [namespace qualifiers $name]
    if {$ns eq ""} {
        set ns [uplevel 1 {namespace current}]
    }
    set name [namespace tail $name]

    Coroutine ${ns}::$name $command {*}$args
}

To provide precisely the same functionality as yield it is necessary to strip off a single layer of list:

proc ::yield2 {value} {
    return [lindex [::yield $value] 0]
}

No other changes are necessary. More likely, one would define ::yield like that, and create a new ::yield-variant which returned the whole invocation arg list.

CASE 2: [Coroutine] in [coroutine] - implementing multi-arg'd coroutines over singe-arg'd coroutine

proc Coroutine {name command args} {
    set ns [namespace qualifiers $name]
    if {$ns eq ""} {
        set ns [uplevel 1 {namespace current}]
    }
    set name [namespace tail $name]

    set coco [::coroutine ${ns}::_C$name $command {*}args]
    trace add command ${ns}::_C$name delete "rename ${ns}::name {}"
    proc ${ns}::$name {args} {
        set name [lindex [info level 0] 0]
        set ns [namespace qualifiers $name]
        if {$ns eq ""} {
            set ns [uplevel 1 {namespace current}]
        }
        set name [namespace tail $name]
        
        ${ns}::_C$name $args
    }
}

Counterfactual side-benefits: ::yield stays exactly as it is.

Yield to populate args becomes simpler.

proc yieldv {value args} {
    uplevel 1 [::yield $value] {*}$args
}