Lightweight Namespaces

New in Jim Tcl v0.73 is (optional) support for namespaces.

The following is taken directly from README.namespaces in the Jim Tcl repository.


Lightweight Namespaces for Jim Tcl

There are two broad requirements for namespace support in Jim Tcl.

This proposal addresses both of these requirements, with the following additional requirements imposed by Jim Tcl.

To further expand on requirement (2), the goal is not to be able to run any Tcl scripts using namespaces with no changes. Rather, scripts which use namespaces in a straightforward manner, should be easily ported with changes which are compatible with Tcl.

Implicit namespaces

Rather than supporting explicit namespaces as Tcl does, Jim Tcl supports implicit namespaces. Any procedure or variable which is defined with a name containing ::, is implicitly scoped within a namespace.

For example, the following procedure and variable are created in the namespace ‘test’

proc ::test::myproc {} {
  puts "I am in namespace [namespace current]"
}
set ::test::myvar 3

This approach allows much of the existing variable and command resolution machinery to be used with little change. It also means that it is possible to simply define a namespace-scoped variable or procedure without first creating the namespace, and similarly, namespaces “disappear” when all variables and procedures defined with the namespace scope are deleted.

Namespaces, procedures and call frames

When namespace support is enabled (at build time), each procedure has an associated namespace (based on the procedure name). When the procedure is evaluated, the namespace for the created call frame is set to the namespace associated with the procedure.

Command resolution is based on the namespace of the current call frame. An unscoped command name will first be looked up in the current namespace, and then in the global namespace.

This also means that commands which do not create a call frame (such as commands implemented in C) do not have an associated namespace.

Similarly to Tcl, namespace eval introduces a temporary, anonymous call frame with the associated namespace. For example, the following will return “::test,1”.

namespace eval test {
	puts [namespace current],[info level]
}

Variable resolution

The variable command in Jim Tcl has the same syntax as Tcl, but is closer in behaviour to the global command. The variable command creates a link from a local variable to a namespace variable, possibly initialising it.

For example, the following procedure uses ‘variable’ to initialise and access myvar.

proc ::test::myproc {} {
  variable myvar 4
  incr myvar
}

Note that there is no automatic resolution of namespace variables. For example, the following will not work.

namespace eval ::test {
  variable myvar 4
}
namespace eval ::test {
  # This will increment a local variable, not ::test::myvar
  incr myvar
}

And similarly, the following will only access local variables

set x 3
namespace eval ::test {
	# This will increment a local variable, not ::x
	incr x
	# This will also increment a local variable
	incr abc::def
}

In the same way that variable resolution does not “fall back” to global variables, it also does not “fall back” to namespace variables.

This approach allows name resolution to be simpler and more efficient since it uses the same variable linking mechanism as upvar/global and it allows namespaces to be implicit. It also solves the “creative writing” problem where a variable may be created in an unintentional scope.

The namespace command

Currently, the following namespace commands are supported.

namespace children, exists, path

With implicit namespaces, the namespace exists and namespace children commands are expensive to implement and are of limited use. Checking the existence of a namespace can be better done by checking for the existence of a known procedure or variable in the namespace.

Command resolution is always done by first looking in the namespace and then at the global scope, so namespace path is not required.

namespace ensemble

The namespace ensemble command is not currently supported. A future version of Jim Tcl will have a general-purpose ensemble creation and manipulation mechanism and namespace ensemble will be implemented in terms of that mechanism.

namespace import, export, forget, origin

Since Jim Tcl namespaces are implicit, there is no location to store export patterns. Therefore the namespace export command is a dummy command which does nothing. All procedures in a namespace are considered to be exported.

The namespace import command works by creating aliases to the target namespace procedures.

namespace forget is not implemented.

namespace origin understands aliases created by namespace import and can return the original command.

namespace unknown

If an undefined command is invoked, the “unknown” command is invoked. The same namespace resolution rules apply for the unknown command. This means that in the following example, test::unknown will be invoked for the missing command rather than the global ::unknown.

proc unknown {args} {
	puts "global unknown"
}

proc test::unknown {args} {
	puts "test unknown"
}

namespace eval test {
	bogus
}

This approach requires no special support and provides enough flexibility that the namespace unknown command is not implemented.

Porting namespace code from Tcl to Jim Tcl

For most code, the following changes will be sufficient to port code.

  1. Canonicalise namespace names. For example, ::ns:: should be written as ::ns or ns as appropriate, and excess colons should be removed. For example ::ns:::blah should be written as ::ns::blah (Note that the only “excess colon” case supported is ::::abc in order to support [namespace current]::abc in the global namespace)

  2. The variable command should be used within namespace eval to link to namespace variables, and access to variables in other namespaces should be fully qualified

Changes in the core Jim Tcl

Previously Jim Tcl performed no scoping of command names. i.e. The ::format command was considered different from the format command.

Even if namespace support is disabled, the command resolution will recognised global scoping of commands and treat these as identical.


comments powered by Disqus