Version 6 of Anonymous coroutine

Updated 2015-11-11 14:35:41 by pooryorick
# returns an unused procedure name
proc gensym {prefix} {
    tailcall apply {{prefix} {
        string cat $prefix[llength [info commands $prefix*]]
    }} $prefix
}
 
# creates an anonymous coroutine. New features:
#  . avoid polluting the global namespace
#  . return a FQ command name, usable in any context

namespace eval ::goro {}
proc go {cmd args} {
    set name [gensym ::goro::#]
    coroutine $name $cmd {*}$args
    return $name
}

Credit for the above code goes to aspect.

The odd construct with tailcall apply is so that info commands will be executed in the caller's namespace.

MS finds that this construct has a big problem: name reuse. Some code could be holding onto a coro handle, and wanting to find out if it still exists via [info command] (or just waiting to error out, intentionally or not). If the coro exited and a new one was created with the same name, chaos could ensue ...

So my proposal is to either use a global counter (possibly in a namespace). If we really cannot live with a global var, we could do:

coroutine gensym apply {{} {
    set prefix [yield]
    while 1 {
        set name $prefix[dict incr counters $prefix]
        if {[llength [uplevel 1 [list info commands $name]]]} continue
        set prefix [yield $name]
    }
}}

aspect: great observation! I've previously used a [gensym] like the above, but switched to the "less stateful" one above for exploring CSP. The risk of hard-to-diagnose errors is a Very Good Reason to avoid name reuse.

MS: modified gensym to take care of emiliano's observation in the chat: we should verify that the command does not already exist.