subcommands

Richard Suchenwirth 1999-07-26 - One way of structuring a command's interface is by adding a subcommand (also known as minor command; examples are "string length" etc., or all Tk widget commands). You'd typically use a switch to dispatch to the appropriate branch of code. Then there should also be a "default" clause, in case an invalid subcommand was given.

The following code was written to handle this situation. It acts much like a switch, but generates an error message that lists all the legal subcommands. This reduces the work, and possible error reason, of having to update the error message when new subcommands are introduced.

 proc subcommands {cmd content} {
    array set a $content
    if [info exists a($cmd)] {
        return [uplevel 1 $a($cmd)]
    } else {
        set cmds [join [lsort [array names a]] ", "] ;#(1)
        set cmds [linsert $cmds end-1 or] ;#(2)
        return -code error "Bad option \"$cmd\": must be $cmds"
    }
 }

Usage example:

 proc try {cmd args} {
     subcommands $cmd {
         foo  {puts bar}
         bar  {puts foo}
         args {puts "my args were:$args"}
     }
 }

  try baz
  ==> Bad option "baz": must be args, bar, or foo        

(1,2): example of how "shimmering" (changing an object from one to another representation) can help to make compact code: joining the sorted list with ", " (i.e. appending commas to all but the last element) turns it into a string, linserting "or" at the last-but-one position makes it a list again, before the formatting of the error message finally makes it a string. Pure-list or pure-string approaches would be much more clumsy, I guess. The cost in runtime is neglegible for lists of O(10) elements. In production code you may gain performance by avoiding shimmering, but it's not in general a Bad Thing ;-)