Ensemble

Purpose: discuss the concept of command ensemble. See also namespace ensemble (Tcl 8.5 command). Note also that itcl has an ensemble command. It would be appropriate to discuss it on this page (perhaps in its own section later?)

This concept has also been called "major/minor" commands by some people (like myself) who didn't know the fancier name of ensemble.

[Question - is this what other languages may call mixins?] [From what I can tell, the term mixin seems to apply to object systems in which one can create objects which inherit from several classes - mixing in the features one wants from any of them, plus adding new features unique to the class.]

Larry Smith No, it isn't. A "mixin" is a class designed to be added promiscuously to any other class in order to add its functionality to that class, without necessarily having anything to do with that class's own function. Classes are normally in a hierarchy and are further and further specialized - object>number>integer>bigint for example. A typical mixin example might enable a class using it be made into a linked list, such that in addition to the normal class methods, it might also have next(...) and prev(...) methods. These methods might have nothing else to do with the functions of that class, but they are "mixed in" to provide a specific feature. This concept is somewhat controversial, it requires multiple inheritance to implement it, and many object-oriented purists contend (admittedly with some justice) that multiple inheritance is to object-oriented programming what GOTO is to structured programming. Any time a new OO language is defined, the first question that must be asked is "Will it support multiple inheritance" because that will classify the result into one of the two fundamentally different paradigms of OO programming. Do you want to be Smalltalk (single inheritance, no mixins) or C++ (multiple inheritance, along with every other feature ever conceived or discovered accidentally)?

Tcl has several kinds of commands.

The first, a simple command like set, provides a very simple interface. One passes either one or two arguments. Depending on the number of arguments, set either outputs the value of the variable, or it sets the variable to a value.

A slightly more complex variation of this is a command that takes arguments. puts for instance has an optional -nonewline flag as well as an optional output channel and an output string.

Next in complexity comes the ensemble. An example of this command would be string. What makes it more complex is that string is an umbrella name for a variety of related functionality. String is the major command name, the minor subcommand names are things like bytelength,compare, etc.

See TIP http://purl.org/tcl/tip/112.html for a TIP to include support for developing this type of command in Tcl.


Since every object command is an ensemble, having support for ensembles as part of the language (presumably coded in C) would make pure Tcl object and megawidget frameworks much more efficient. -- WHD


LV So how do you document extendable ensembles? For instance, let's say we create a new command called xtsil. It's designed to be extendable from either script or some API. The original command's documentation would be xtsil in section n. Now assume that J. Random Programmer (JRP) comes along with an implementation of xtsil stdev. Certainly s/he could contribute the code to the original author for integration. But that may not always work - two people might have alternative implmentations (or even different functional implmentations for the same subcommand name).

In some languages, each subcommand gets its own reference doc file, which takes care of this problem (but does result in a LOT of reference docs).

WHD: I'd argue that extending someone else's ensemble in the manner you describe is a no-no, as it muddies the water for maintenance developers. That is to say, even if the capability existed, I wouldn't use it. I might define my own ensemble which delegates most of its subcommands to some other ensemble, and adds a few of its own; and I'd document by saying "myxtsil is just like xtsil, but adds these subcommands...."

LV: Then in your opinion, the only reason to have such a command is for implementing a new command?

RS notes that BWidget constructs its method names in the pattern Class::method, therefore enhancing BWidget is very easy: by just writing a proc

 proc Foo::dance {self what} {...} 
 ;# ...you can instruct an object of class Foo to:
 myFooInstance dance polka

GN There is nothing special about BWidget in this respect, XOTcl does it as well since ages and provides an unknown method for each object/namespace (see XOTcl Objects as Tcl Commands with subcommands).


LV Then how should one document the dance method for this object? Should each object have a separate reference page for each method (whether HTML, man, or some other format) so that all one needs to do is add a new page?


See Wrapping Commands, stacking for some examples of creating ensembles.


RFox The issue of merging documentation for random programmer implemented subcommands in an ensemble with the original package documentation is only important if these subcommands get contributed back to the original developers. Now if, instead of using those drudgy man pages, we started using Wiki based documentation (as some starkits do), all this is simple, just edit the wiki page that documents the ensemble and add in your subcommnds. Now you've got the local site documents updated.

In any open source system that accepts external contributions, there's always some release manager/management and part of the job of the release manger is merging documentation from the various sources.


LV It seems like most developers wouldn't bother to track every ensemble change that every package require invoked might perform - or the internal ones that each one used do - and do all that doc integration.


EKB In case this isn't already well known, it's easy to create an ensemble with snit:

 snit::type mytype {
    option -greeting "Hi!"
    option -parting "Bye!"

    method greet {} {
      return $options(-greeting)
    }
    method part {} {
      return $options(-parting)
    }
 }

Then use it, e.g.,

 mytype myobj -greeting "Hello!"
 puts [myobj greet]
 puts [myobj part]

It can then be extended by delegation. My comment on that discussion is that it seems best to give an extended ensemble a new name, as a way to relieve the documentation headaches.

  Steps for one proc per subcommand

HaO An application of ensembles is to have a proc per subcommand. Here are the necessary steps:

namespace eval ::commandname {
    namespace export sub1 sub2
    namespace ensemble create
}
proc ::commandname::sub1 {args} { puts sub1=$args }
proc ::commandname::sub2 {args} { puts sub2=$args }

And here is how to call them:

% commandname sub1 a b c
sub1=a b c

As a more practical example, use a command counter, which may set, get or increment a number:

namespace eval ::counter {
    variable counter 0
    namespace export get set incr
    namespace ensemble create
}
proc ::counter::get {} {
    variable counter
    return $counter
}
proc ::counter::set {value} {
    variable counter
    # the "::" in front of set is necessary to not call ::counter::set
    return [::set counter $value]
}
proc ::counter::incr {increment} {
    variable counter
    return [::incr counter $increment]
}

And here how to use it:

% counter set 5
5
% counter incr 5
10
% counter get
10

Don't get irritated that ensembles are a mapping of a b -> a::b. To test this:

% namespace eval counter {namespace export showlevel}
% proc ::counter::showlevel {} {return [info level 1]}
% counter showlevel
::counter::showlevel

RS 2015-05-13 A short interactive session to discover which Tcl commands are implemented as ensembles:

% set ens {}; foreach c [info commands] {if ![catch {namespace ensemble configure ::$c -map}] {lappend ens $c}}
% set ens
chan clock info dict string

Note you can use namespace ensemble exists instead of catching the error.

% lmap c [info commands] { if {![namespace ensemble exists $c]} continue; set c}
binary tk array chan info file namespace dict string