namespacestclprocedure

copying a proc to a namespace in TCL


This is a simplified case of a problem. I am trying to copy a procedure to a namespace, so that it would be using the namespace own's context. Using import does not work (probably because it just creates aliases) See code below:

proc me_namespace {} {
 puts "namespace is:[namespace current]"
 puts "called namespace is:[uplevel 1 namespace current ]"
}
namespace eval foo {} {
 me_namespace
 puts "Now import"
 namespace import ::::me_namespace
 me_namespace
}

The code output is:

namespace is:::
called namespace is:::foo
Now import
namespace is:::
called namespace is:::foo
namespace is:::foo

Ideally, the proc me_namespace 1st line output, after the copying, should be:

::::me_namespace

Any ideas? The only thing that I can think of is having the procedures definitions be in a file, and then reading the file, and using eval, but I was looking for something more elegant. The reason that I do not just use uplevel, is that at times (in particular when using variable with uplevel is that the runtime, a times, is far too slow. TCL version is 8.6


Solution

  • Procedures are bound to their containing namespace. Rename the procedure, change its binding. (I hate that!) To recreate a procedure in another namespace, you effectively have to either rename (a move) or to rerun the proc that created the original.

    Now, while I'd really recommend looking at using TclOO when you're getting into this sort of thing, you might be able to use a different technique. The namespace upvar command is reasonably fast, and you might be able to use it like this:

    proc master_version_of_foo {ns args} {
        namespace upvar $ns abc local1 def local2
        # ... continue with your code in here
    }
    
    # Stamp out some copies
    namespace eval ::x {}
    interp alias {} ::x::foo {} ::master_version_of_foo ::x
    namespace eval ::y {}
    interp alias {} ::y::foo {} ::master_version_of_foo ::y
    namespace eval ::z {}
    interp alias {} ::z::foo {} ::master_version_of_foo ::z
    

    These true command aliases are pretty fast, the variable binding is pretty fast too (similar in speed to the global command; they use virtually the same bytecode operations internally) and you'll be sharing the compilation of the procedure which is itself pretty fast.

    The main reason for not doing this is if you need different command resolution in the three cases. At that point, the fastest routes all go through TclOO (which tames quite a few of the advanced capabilities of Tcl to the point of being usable) and probably require some serious redesign on your part to use well.