We have, in 8.5 (now that I've finally got it to play with), the
[namespace ensemble] command, for doing commands of the sort [dict] or
[file]. These use a base command, which takes the thing being
manipulated as an argument to a sub-command. The good part of doing it
this way, is that the thing being manipulated can be either a value or
a name reference, as required by the sub-command. (Or, for that
matter, may not even be required, as in the case of a [thing new]
sub-command.)
But, what's the best way to do the alternative, [$thing command]
style? I generally create a [new] procedure within a namespace, which
creates an array to hold the things innards, and uses [interp alias] to
invoke a [thing::command] procedure within the namespace, that acts as
a sub-command invocation gateway. Some simple stuff it'll usually do
right off the bat with a switch, while more complicated stuff gets
dispatched as a command within the namespace (that rather neatly allows
me to assume some internal naming convention to separate the
sub-commands from first-level command). (A -partial match type to
switch that correctly handles things like "set" vs. "set-here", would
be much appreciated ;) )
I've been looking at improving this with the use of [ensemble]s, but
this won't achieve very much. The [interp alias] way generally
requires that $thing be the first argument before the sub-command.
[ensemble], however, requires that the sub-command be first. So I'd
still need a [thing::command] wrapper to handle the translation,
although now much simpler than before. Or am I missing something?
On the assumption that I haven't, has anyone perchance tip'd an option
to [ensemble create] to let the first n arguments be arguments, with the
sub-command following that? (Or alternatively, an option to specify
which argument is the sub-command, instead of requiring it to be the
first.) Doing that would allow you to create two ensembles, one for
the top-level functions like [new], and a second to handle actions
applied to the commands created by [new], simply by having [new]
generate an [interp alias] to the second ensemble.
Fredderic
I think you'll want to read carefully about the -map option. :-)
Donal.
I've read the text on -map a few times, but I still don't see how it
fits my problem... To clarify a little, if I explained it badly, I
presently have something like this;
proc shoop::New {args} {
set id ::shoop::[incr ::shoop::unique-id]
array set $id $::shoop::TEMPLATE
... do mode stuff ...
interp alias {} $id {} shoop::command $id
return $id
}
set myShoop [shoop::New shoopy args]
$myShoop do something
set blah [$myShoop get something]
I rather like this style. [shoop $id blah] is good too, but annoying.
[$id blah] is much lazier, and I like lazier, it takes a little of the
strain off my not-exactly-21-anymore brain.
The problem with -map is that every time you create a new shoop, or
destroy an old one, you'd have to update the map. And even then, it
still needs an [interp alias] to set up, and you still need to have it
invoke a common command, possibly another ensemble, that in turn
invokes the sub-command you're asking for.
As far as I can tell, an intermediary is still required to change
[shoop $id sub args] into [shoop sub $id args], before the ensemble can
properly handle it. Otherwise, [ensemble -map] will still see my $id as
the sub-command, and not as an argument to the sub-command, which is
what I need.
As it stands, I actually add another argument to the [interp alias]
line. The number 2. This quite neatly side-steps the need for an
excessive number of uplevel's, if you just increment the value every
time you pass it down into another procedure. You can then [upvar
$lvl ...] or [uplevel $lvl ...], and have the code skip right back to
the invoking procedure. It really works quite well.
Fredderic