TIP #112 Version 2.1: Ensembles are Namespaces are Commands

This is not necessarily the current version of this TIP.


TIP:112
Title:Ensembles are Namespaces are Commands
Version:$Revision: 2.1 $
Author:Donal K. Fellows <donal dot k dot fellows at man dot ac dot uk>
State:Draft
Type:Project
Tcl-Version:8.5
Vote:Pending
Created:Thursday, 10 October 2002

Abstract

This TIP proposes unifying the concept of ensembles (from [Incr Tcl]) with namespaces and commands. It also adds control of command rewriting to allow for more efficient support for object systems like Snit.

Rationale

Tcl's subcommand-style command collections (e.g. array, info, string, interp, etc.) are a very intuitive and popular way of structuring collections of related commands. However, it is quite awkward to write Tcl code that behaves that way. Users of [Incr Tcl] have access to ensembles which provide that, but it would be a very useful feature for many other uses too.

At the same time, it is becoming clear that many applications want to commonly refer to commands inside other namespaces directly (instead of through the [namespace import] mechanism) but the syntax for doing this is verbose and not as elegant as it might be.

I believe that the same solution can address these two problems in one go, and make the language stronger and more usable for it.

Furthermore, by giving the programmer control over the mapping from the ensemble subcommands to their implementing commands, we can build a simple class system on the cheap since we can, in effect, import commands from elsewhere into the ensemble. Indeed, by extending the mapping so that it allows the specification of not just the implementing command, but also some leading arguments to that command (similar to what you can do with the [interp alias] mechanism) this becomes a very powerful mechanism indeed.

Proposed Change

I propose to add a new subcommand to the [namespace] command, ensemble, that creates and manipulates the ensemble command for a namespace. Each namespace may have either 0 or 1 ensemble command associated with it, with the default name of the ensemble command being the fully-qualified name of the namespace itself, though it will be legal to rename the ensemble command (anyone wanting to track such events should use the [trace] command.) Tcl will not create an ensemble command for any namespace by default.

The format of the [namespace ensemble] command will be this:

namespace ensemble theNamespace ?theSubcommandToCommandPrefixDict?

That is to say that the first argument to the [namespace ensemble] command will be the name namespace to work with, and using the command will cause the ensemble command (with the corresponding name, as outlined above) to be created if it does not already exist, optionally specifying a dictionary (as in TIP #111) that maps from the full ensemble subcommand name to the prefix command and arguments used to replace the ensemble command/subcommand parameter pair. If no dictionary is provided or the dictionary is empty, the ensemble's subcommands shall be exactly those commands that are exported by the namespace at the time that the ensemble command is called. The result of calling the command will be the dictionary provided, or an empty dictionary if no dictionary was provided.

If the ensemble command already exists and a dictionary, possibly empty, was specified, this shall update the set of commands provided by the ensemble command (with an empty dictionary standing for the set of exported namespace commands, as described in the previous paragraph) and return the new dictionary. If the ensemble command exists but no dictionary is provided, then this command will return the currently used dictionary (subject to the special interpretation of the empty dictionary) without any side effects.

Given an ensemble command created by the above mechanism, calling the command will first of all match the subcommand to its implementing command (or command/argument list, as derived from the dictionary) in a manner that will be recognizably similar to that enforced by Tcl_GetIndexFromObj() (including unambiguous prefix extension). Then the ensemble command will rewrite the command and arguments so that the ensemble command and subcommand are replaced by the implementing command and any specified arguments, with the resulting word list being fed to Tcl_EvalObjv() for execution. Note that this does not increase the stack depth in terms of [uplevel], and that the implementing command may itself be an ensemble command.

Examples

namespace eval carrot {          ;# Creates command ::carrot
   namespace export foo bar potato

   proc foo {} {puts 1}          ;# Exported
   proc bar {} {puts 2}          ;# Exported
   proc boo {} {puts 3}          ;# Not exported

   namespace eval turnip {       ;# Not exported
      namespace export alpha
      proc alpha {} {puts 4}     ;# Exported
      proc beta {} {puts 5}      ;# Not exported
   }
   namespace ensemble turnip

   namespace eval potato {       ;# Exported
      proc north {} {puts 6}     ;# Not exported
      proc south {} {puts 7}     ;# Not exported
   }
}
namespace ensemble carrot
namespace ensemble carrot::potato {north ::carrot::potato::north}


carrot foo                       ;# Prints 1
carrot bar                       ;# Prints 2
carrot b                         ;# Also prints 2 ("boo" not exported)
carrot ?                         ;# Alternatives "bar", "foo" and "potato"
carrot potato                    ;# Complains about missing argument
carrot potato ?                  ;# Suggests you might try "north" instead
carrot potato north              ;# Prints 6
carrot turnip alpha              ;# Complains about "turnip" being not known
carrot::turnip alpha             ;# Prints 4
carrot::turnip::beta             ;# Prints 5

rename ::carrot::potato ::spud
spud north                       ;# Prints 6
spud south                       ;# Complains about "south" being not known
carrot potato north              ;# Complains: no ::carrot::potato command
namespace ensemble carrot::potato {north {puts NORTH} south {puts SOUTH}}
spud north                       ;# Prints NORTH
spud south                       ;# Prints SOUTH
namespace delete carrot
spud north                       ;# Illegal: spud command already deleted


namespace eval A {
   proc a args {puts A::a=>$args}
}
namespace eval B {
   proc b args {puts B::b=>$args}
}
namespace ensemble A {
   eg1 {::A::a foo bar}
   eg2 {::B::b 1 2 3}
   eg3 ::string
}
A eg1 spong                      ;# Prints A::a=>foo bar spong
A eg2 evil code {[exit]}         ;# Prints B::b=>1 2 3 evil code [exit]
A eg3 length qwertyuiop          ;# Returns 10

Consequences

Many commands in both Tcl and Tk would benefit from leveraging this, and it would enable straight-forward implementations of things like TIP #65 in pure Tcl code. It would also make doing things like partial exposure of ensemble-like commands in safe interpreters much easier.

Copyright

This document has been placed in the public domain.


Powered by TclThis is not necessarily the current version of this TIP.

TIP AutoGenerator - written by Donal K. Fellows