Version 4 of multi-arg coroutines

Updated 2010-04-15 03:51:33 by CMcC

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

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

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
}