Updated 2015-12-06 17:49:53 by pooryorick
# returns an unused command 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.

PYK 2015-11-11:

The tailcall apply trick doesn't work as described because a 2-element procedure specification, is applied in the global scope. It could be reworked to use uplevel to get the namespace of the caller, but in that case it would be simpler just to uplevel the info commands call, and maybe use namespace which instead. I sometimes reach for info cmdcount to address the issue of name reuse. Combining these ideas leads to an implementation like
proc gensym prefix {
    while {[uplevel [list namespace which $prefix[
        set cnt [info cmdcount]]]] ne {}} {}
    return $prefix$cnt

go still creates a named coroutine, not an anonymous one, and it's straightforward to call coroutine directly with a gensym substitution as the first argument so that the results of that first coroutine call are available as needed:
set res [coroutine proc1 coro[gensym] arg1 arg2 ...]