Major/Minor Commands in TCL David Herron 24-June-1993 INTRODUCTION At the TCL conference a group of us were sitting with Dr. John chatting about, guess what, TCL. At one point the subject of how to do modules came up and one person requested the ability to create `widgets' using just TCL. In particular he wanted `mega widgets' which had a programming interface very extremely similar to existing widgets. The reason is one of familiarity. We are already very familiar and comfortable with the programming interface of existing widgets. This is very different from the other module systems I've seen in TCL since they all had very different programming interface from widgets. After some discussion John was strongly suggesting hacking on the frame widget. My addition to the suggestion was to attach an interpreter to the frame widget (using minterp) since then you could hide the implementation details. It might even be easy since minterp already had facilities for exporting `methods' from an interpreter. And this is what I went home with the idea of doing. After a lot of thought I came to the strategy followed here. The basic idea is to take the major/minor concept present in many TCL commands, and to formalize it to be manipulatable from TCL. Currently the fact that a particular command takes minor commands is an accident of the implementation of its handler function. Well, purposeful accidents are still accidents sometimes. With the major/minor extension the minor commands become a list of minor command names which can be manipulated from TCL or C. From TCL the minor commands look very much like proc's and act very similarly. Creating a major command is just a matter of subcmd addmajor procName { TCL code to be executed when procName is called with no arguments } Then to add minor commands to it subcmd add procName minorName { argument list } { TCL code to be executed on: procName minorName argument list } So far that's simple enough and only a little bit interesting. Where it becomes interesting is in two proposed uses: 1: The core commands which use minor commands can be changed to use this major/minor stuff. This allows those commands to be extended or changed by third party extensions. For instance, the Extended TCL package could extend the `file' command this way without having to modify the core of TCL. Also the `info' command could be used by all extensions to return information instead of TK having its own `winfo' and extended TCL having its own `xinfo'. But to do this right would require another level of minor commands for `info packageName minorCmdName'. 2: All the widget commands in TK can be `subclassed' to change or add minor commands. Which brings us to the original request above. 3: A light-weight form of near-`objects' can be created. An unfortunate thing is that Proper Implementation of this facility required modification of the core of TCL. The modifications are extremely localized to tclProc.c and add very little new code. On a SPARC using gcc v2.4.3 with -O2 and -g the size differences are: BEFORE: text data bss dec hex 2432 112 0 2544 9f0 AFTER: text data bss dec hex 5688 184 0 5872 16f0 Diff: 3256 72 A full implementation would make more changes throughout the TCL core modifying things like the `file' and `info' commands. Then over in TK I've currently only modified the frame widget so its widget commands are `major commands'. This is enough to create some sample mega widgets. The size differences are (with only -O for some reason): BEFORE: text data bss dec hex 2280 464 0 2744 ab8 AFTER: text data bss dec hex 2288 464 0 2752 ac0 Diff: 8 0 Not a whole lot of difference, eh? The only changes was adding a call to Tcl_CreateMinorCommand() and then changing the if-then-else string into a switch statement. For more complicated widgets there are more calls to Tcl_CreateMinorCommand() or maybe adding a loop and array like I do with the `subcmd' command. Combining this with the `varframe' extension posted approx a week ago would be way cool. (And almost completely remove the need for `minterp', sigh...) THE `subcmd' COMMAND Subcmd is the entry point into manipulating major/minor commands. Any command which is a major command can be manipulated this way. subcmd addmajor majorName body Adds a new major command. The `body' is a TCL command string executed when majorName is called with no arguments. A local variable named `majorCommand' is created and holds the name of the major command being executed. subcmd add majorName minorName args body Adds a new minor command to an existing major command. The argument list and body are processed just like a `proc'. The majorCommand variable is created like above. subcmd rename major oldminor ?newminor? Changes the name of a minor command. If newminor is not given then the minor command is deleted. subcmd exists major minor Tests for the existance of the minor command. subcmd list major Returns a list of the minor commands of the given major command. EXAMPLE #1 -- counter The subject matter is specifically borrowed from one of the examples in TclPackage. It shows creation of an object-like thing. Except it pollutes the global namespace a bit (the counters array) and in general doesn't hide implementation very well. wish: Counter a wish: a 0 wish: a -- -1 wish: a -- -2 wish: a -- 5 -7 wish: a ++ 20 13 wish: a += 20 33 wish: a -= 20 13 EXAMPLE #2 -- flistbox This is a `fancy listbox'. It is a hierarchy of frames with a couple of scrollbars. The minor commands ensure the scrollbars show only when they are really and truly needed, all other times they are gone. Most of the minor commands come from the listbox widget's minor commands. The advantage here is that the internal widget hierarchy is hidden from view. There's also the nicety of the auto now-you-see-them now-you-don't scrollbars which I've missed terribly from having programmed with Motif. INSTALLATION Unpack an unmodified TCL 6.7 and TK3.2 distribution. I have mine in sibling directories rather than the tcl directory being a child of tk3.2, and the diff's reflect this. Type: patch -p1 -l < patch-file Then edit the Makefiles, configure and compile as normal. Both tclTest and wish will have the subcmd command. FUTURE Well, I don't know how much future this will have if John doesn't incorporate it into TCL for v7.x. But I realize the timing issues of trying to get 7.0 *and* the book out the door. If this were in v7 then the book would have to change, delaying the whole ball of wax. But in my judgement this can be a really important addition to TCL. Because of its nature it would be best if it were incorporated into the core TCL, especially since it is so small. Probably the greatest importance of this is in aiding integration of third party packages so that they look more like core TCL. Face it, `winfo' and `keylget' kinda look like tacked on afterthoughts. In any case one thing to do is finish modifying all the relavent TCL and TK commands to be major commands. Another thing is more examples. Expect a version of minterp to be available using major commands. Doing this will decrease the size and complexity of minterp plus add some interesting new abilities to it. A few ideas were left unimplemented: - Unknown minor commands. Right now an error is simply returned. There should be an `unknownminor' command, or some such, which can perhaps patch in the minor command on demand. - Better errorInfo. As it is winding out of the procedure calls, some errorInfo should be added about which minor command is being executed. - Sharing of common minor commands. Take the Counter example. For each Counter instance the minor commands are completely duplicated. In each one is a hash table entry, a MinorCmd structure and a duplicate of the procedure body. Is there some way these can be shared? - Before and after hooks on minor commands. These might be useful in some subclassing cases. Consider: subcmd beforeHook file exists {file} { do something interesting } ...versus... subcmd rename file exists _exists subcmd add file exists {file} { do something interesting file _exists $file } Hmm... - Ability to retrieve body and argument list of major and minor commands. - Rename `lappend', `lreplace' and so forth to be a major/minor setup. (Allowing for the Extended TCL tkey'd list commands to hook in as new minor commands). - Verification that a given command is, indeed, a major command before adding minor commands to it. - A way to test if a command is a major command. - Documentation for the C API. - Optimization.