Version 90 of namespace

Updated 2014-06-23 20:12:39 by pooryorick

namespace is a built-in ensemble of commands for working with namespaces.

Synopsis

namespace children ?namespace? ?pattern?
namespace children ?namespace? ?pattern?
namespace code script
namespace code script
namespace current
namespace current
namespace delete ?namespace namespace ...?
namespace delete ?namespace namespace ...?
namespace ensemble option ?arg ...?
namespace ensemble option ?arg ...?
namespace eval namespace arg ?arg ...?
namespace eval namespace arg ?arg ...?
namespace exists namespace
namespace exists namespace

output:

namespace export ?-clear? ?pattern pattern ...?
    :   '''[namespace forget]''' ''?pattern pattern ...?''
From 8.5 onwards, use this instead:
    :   '''[namespace import]''' ''?-'''''force'''''? ?pattern pattern ...?''
namespace inscope namespace script ?arg ...?
namespace origin command

list the variables in a namespace:

namespace parent ?namespace?
    :   '''[namespace path]''' ''?namespaceList?''
list the procs in a namespace:
    :   '''[namespace qualifiers]''' ''string''

    :   '''[namespace tail]''' ''string''

    :   '''[namespace upvar]''' ''namespace otherVar myVar ?otherVar myVar ...?''

    :   '''[namespace unknown]''' ''?script?''

    :   '''[namespace which]''' ''?-'''''command'''''? ?-'''''variable'''''? name''



** See Also **
::

list the commands in a namespace:

namespace size
Thoughts on Namespaces and OO
Namespace variables 2
Namespace resolution of Variables & Procedures
data is code
_namespace import_ and _rename_
describes the extraordinary freedom won through the use of namespaces.

Documentation

official reference documentation
Namespaces and Packages , by William Duquette
XML Namespaces FAQ
valuable background information on the general use of namespaces in computing
A Gentle Introduction to Namespaces , by Sam Ruby 2002
an XML-focused description

Introspection

Can someone discuss how a Tcl script can determine:

  • what namespaces are available at any particular point in time
  • what procs and variables available in any one of these namespaces

A simple proc to list all child namespaces, recursively:

proc listns {{parentns ::}} {
    foreach ns [namespace children $parentns] {
        eval lappend result [listns $ns]
        lappend result $ns
    }
    return $result
}

output:

% namespace eval ::test {set foo 1}
1

% namespace eval ::test2 {set foo 1}
1

% namespace eval ::test::test3 {set foo 1}
1

% listns
::test::test3 ::test ::test2 ::tcl

From 8.5 onwards, use this instead:

proc listns {{parentns ::}} {
    set result [list]
    foreach ns [namespace children $parentns] {
        lappend result {*}[listns $ns] $ns
    }
    return $result
}

list the variables in a namespace:

proc listnsvars {{ns ::}} {
    return [info vars ${ns}::*]
}

list the procs in a namespace:

proc listnsprocs {{ns ::}} {
    return [info procs ${ns}::*]
}

list the commands in a namespace:

proc listnscommands {{ns ::}} {
    return [info commands ${ns}::*]
}

The previous examples only work well with fully qualified namespace names.

Beware that in some apps these may take a long time to complete (for example, if there are hundreds of thousands of commands in ::).

Avoiding variable declarations in procedures

JCW wrote in comp.lang.tcl: For variables, I've found a child namespace to be very convenient. Instead of:

namespace eval TO {
    variable foo
    proc proc1 {} {
        variable foo
        ... use $foo ...
    }
}

Consider using:

namespace eval TO {
    namespace eval v {
        variable foo
    }
    proc proc1 {} {
        ... use $v::foo ...
    }
}

No more lists of variable calls inside each proc. The other way is to put all your variables inside a single array, but my impression is that the v::* approach is slightly faster (variable seems to take a bit of time, don't know why).

Procedure Resolution

When referring to a variable or a proc in a namespace embedded in other namespaces, Tcl first searches the current namespace and then the global namespace (as documented) and doesn't look in any intervening embedding namespaces:

proc ::p {} {puts {I'm ::p}}
namespace eval ns {
    proc p {} {puts {I'm ns::p}}
    namespace eval child {
        p
    }
}

Result:

I'm ::p

Tcl resolved the name in the global namespace and didn't search in the intervening ns namespace.

This behaviour can be modified via namespace path.


LVwikignoming 2010-07-01 13:03:52:

A recent email on the TCLCORE mailing list discussed a problem. The user was attempting to add a new subcommand array, but the code was generating peculiar results. The solution, as provided by dkf, was to change the proposed code so that the proc was using ::set instead of just plain set. The reason was that the ::tcl::array namespace already had a set that was overriding ::set and resulting in the unexpected behavior.

So, this note is not specific to set, but applies in general. If you are seeing behavior that reports a problem that is unexpected, examine the code closely to see if you need to add namespacing to the commands in use.

Copying namespaced commands to other namespaces

kruzalex 2013-04-04:

Currently, the typical idiom for importing commands as functions into tcl::mathfunc is to use interp alias, which has the nasty effect of incurring some performance degradation on each invocation of the alias.

If the source command has been exported from its own namespace, then it can be imported into another, which execution of the imported command considerably faster, and makes the new command robust against later renaming of the origin command.

Sometimes one might wish for more than what namespace import currently provides:

  1. one might want to access commands from a namespace where they are not exported from. e.g.: ::tcl::unsupported doesn't export anything, so disassemble cannot just be imported.
  2. one might want to import commands to a different name, e.g., importing tcl::string::length to a local strlen
  3. a special one: if one tries to import only the multiplication operator from ::tcl::mathop::* then the asterisk is interpreted as a pattern, and everything is going to get imported.

I've written a procedure that looks a bit complicated/awkward, but addresses all these shortcomings. Here it is:

proc alias {src tgt} {
    set nsrc [namespace qualifiers $src]
    set tsrc [namespace tail $src]
    set oe [uplevel 1 [list namespace eval $nsrc {namespace export}]]
    uplevel 1 [list namespace eval $nsrc {namespace export *}]
    set n 0
    while {[namespace exists tmp-[incr n]]} {}
    set fqsrc [uplevel 1 [list namespace which $src]]
    namespace eval tmp-$n [list namespace import $fqsrc]
    uplevel 1 [list rename [namespace which -command "tmp-${n}::$tsrc"] $tgt]
    uplevel 1 [list namespace eval $nsrc [list namespace export -clear {*}$oe]]
    namespace delete tmp-$n
}

PYK 2014-06-23: See also proc alias for another implementation of the same.

Since Tcl8.6, you could create this procedure named as ::tcl::namespace::alias instead, and add it to the ensemble with three more awkward lines:

set nsmap [namespace ensemble configure namespace -map]
set nsmap [dict merge $nsmap [dict create alias ::tcl::namespace::alias]]
namespace ensemble configure namespace -map $nsmap

In Tcl8.5 namespace isn't a namespace ensemble yet, but the global proc alias works just fine with Tcl8.5.

Examples:

# import "string length" as "strlen" to be used within expr
alias ::tcl::string::length ::tcl::mathfunc::strlen

# save some typing in an interactive shell :)
alias ::tcl::unsupported::disassemble disasm

Note: unlike interp alias, you can not append any extra arguments with this "alias". However, if the intended argument is really just the subcommand of an ensemble (as is string length), then you can simply alias the subcommand directly :-)

Known bugs: specifying bad arguments may leave some empty namespace and export-patterns behind. That could be fixed with try or catch, but I thought it was already complicated enough as it is without fancy error handling. Also, it would be nice to be able to specify more src/tgt pairs on one invocation...

kwilsoft The copied command is still the imported command from the origin namespace. Hence, when removing command from origin namespace, the alias command in will also cease to work.

Misc

JeremyM 2007-09-19: I'm struggling with the decision as to whether to use a namespace or an object to hold code and variables that are a singleton, i.e. if I use an object, I will always instantiate exactly one instance of the object. Because of this, a namespace seems like a more appropriate container, but I'm getting the feeling that namespaces are somewhat obsolete. Plus I'm not crazy about mixing the two syntaxes in one set of scripts. So the question is, what is the best way to hold a block of code and variables that are a singleton?

Bryan Oakley 2007-09-19: There is no definitive "best way". I use namespaces all the time. I even use them for "objects" that aren't singletons. I've done it so many times I can (almost, but I keep trying!) do it with my eyes closed. Namespaces are much more portable than objects since, as of this writing, they are part of the core and OO is not. If portability is not a concern and you already use an object system, it makes sense to continue to use an object system.

PYK 2014-06-23: namespace isn't obsolete, just lower-level.

Private namespaces

Anonymous: There should be a facility in Tcl to define a private namespace. And, in a completely different direction, Darren New points out that tcllib would be a good home for a namespace serializer, that is, a proc which writes out a namespace's content in a form that can be read back in to recreate the namespace exactly.

Stu 2007-09-30: Creating a randomly named namespace with global vars cleanup. Left-to-right evaluation and the fact that for and unset return empty strings are what makes it possible.

# Random namespace (10 chars, A-Z) with vars cleanup
namespace eval [
    for {set ns {};set i 0} {$i < 10} {incr i} {
        append ns [format %c [expr {65 + int(rand() * 26)}]]
    }
][unset i][set ns][unset ns] {
        puts [namespace current]

        # Other stuff: variables, procs, etc.
}

Lars H, 2007-10-02: Edited the above to make it more readable. Technically it's sufficient that unset returns an empty string, as the first three command substitutions in the namespace argument of namespace eval could be combined into one. Realistically, the for loop should be in a procedure instead to avoid using global variables at all...

Stu 2008-11-16: Lars, could you please date your comments? The one you added below in the middle of the paragraph arrived a year after the original discussion, when apply did not even exist. So yes, now with Tcl 8.5, it can be done with apply ... I guess ... you didn't provide an example.

Stu 2007-10-02: But where would you put the command? The idea here is to pollute the global namespace as little as possible and to create a unique namespace so that we're not "stepping on anyone's toes." (Lars H: Well, use apply then.) If one writes namespace-agnostic code then one should be able to source their code into unknown environments without problems. Even with a proc, we still pollute the global namespace - one proc versus two variables seems like a step in the right direction.

This is a bit better (I like it in one line but this is easier to read/understand):

# Random namespace (10 chars, A-Z) with cleanup, using [proc] with empty proc name.
namespace eval [
    proc {} {} {
        set ns {}
        for {set i 0} {$i < 10} {incr i} {
            append ns [format %c [expr {65 + int(rand() * 26)}]]
        }
        return $ns
    }
][{}][rename {} {}] {
        puts [namespace current]
        # Other stuff: variables, procs, etc.
}

Using eval, I can eliminate all procs and vars and it's much smaller more readable:

# Random namespace (10 chars, A-Z) with cleanup.
namespace eval [eval return [string repeat {[format %c [expr {65 + int(rand() * 26)}]]} 10]] {     
    puts [namespace current]
    # Other stuff: variables, procs, etc.
}

This gives an interesting result which I don't understand just yet. Put the above (with eval) code in a file: namespacetest.tcl

$ tclsh namespacetest.tcl                                                                        
::HTUZNJTKOE

$ tclsh
% source namespacetest.tcl
NIDZGBNLJP

Where is the :: ?

DKF: Seems to me like the return might be confusing things, causing the evaluation to stop before the namespace eval's body fires. In 8.5 use return -level 0 instead.

Stu 2007-10-02 Ok then, (and even better) without eval:

namespace eval [subst [string repeat {[format %c [expr {65 + int(rand() * 26)}]]} 10]] {
    puts [namespace current]

    # Other stuff: variables, procs, etc.
}

Stu 2008-10-31 Ultimate random namespace, like ::TkTwig!(20081031023934)1500<omzpnyxzif>

#! /bin/sh
# \
exec tclsh "$0" ${1+"$@"}

package require Tcl; namespace eval \
MyProg!([clock format [clock seconds] -format %Y%m%d%H%M%S])[pid]<[subst \
[string repeat {[format %c [expr {97 + int(rand() * 26)}]]} 10]]> {


# program goes here


}; #End of namespace
 
# EOF

PYK 2014-06-23: I usually just use info cmdcount

namespace eval [info cmdcount] {
    ...
}

And for good measure, here's an example using apply:

namespace eval [apply [list {} {
    while {[namespace exists [set ns [info cmdcount]]]} {}
    return $ns
} [namespace current]]] {
    #script goes here
}

An Anonymous Critique

This section was transplanted here from Tcl Warts.

The concept is good, but the execution is flawed, especially if you're doing anything object-oriented. Which is kind of ironic, given that (AFAIK) namespace were created to support OO, specifically incr Tcl. The implementation has the fingerprints of C++ all over the syntax. Tcl is many things, but C++ is not one of them. The two don't fit well together.

A problem that I keep running into is that I don't typically use the global scope operator :: for objects at global scope, so there's usually a string inequality between what I'm typing and the value of $self, $this, self, whatever the OO system is using. And if you try to compose object names by combining object names, you get spurious ::'s lying around in the middle of names, which makes Tcl very unhappy.

History

TCLCORE Re: incr Tcl in the core? , tcl-core mailing list, 2000-11-09
KBK recounts the panel discussion at the Third Annual Tcl/Tk Workshop that led to the inclusion of namespace in the core