Version 31 of command

Updated 2014-04-03 19:53:38 by pooryorick

According to the Dodekalogue, in Tcl a command is made of words, and a script is one or more commands, although in practice, scripts are composed of zero or more commands.

Description

Richard Suchenwirth et al:

Tcl is the Tool Command Language. Execution is done by splitting scripts into commands (by newline or semicolon). A command is a sequence of words, the first being the command name, the rest its arguments. A specialized mantra would say everything is a command, even what other languages call "control structures" (if, while, for...) or "declarations" (global, proc, variable...).

The names of all commands available from the current namespace are returned by

info commands

They may have come from several sources:

The following proc returns the type of a command, or an empty string if the name is not a command:

proc command'type2 name {
    foreach {result condition} {
         unsourced {[info commands $name] eq {} 
                    && [info exists ::auto_index($name)]}
         {}        {[info command $name]==""}
         proc      {[info procs $name]==$name}
         alias     {[lsearch [interp aliases {}] $name] >= 0}
         image     {[lsearch [image names] $name] >= 0}
         widget    {[winfo exists $name]}
         command   1
    } {if $condition {return $result}}
} ;# RS

To introspect commands in other than the current namespace, walk the tree with namespace children.

info procs and info commands behave differently. As the above piece of code demonstrates, there are independent uses for the two.


Cameron Laird pointed out:

The succinct command'type2 definition is slightly nonrobust in the face of, for example,

proc tcl* args {}

It's far from alone in that minor weakness, of course

And there is a way to fix this: while [info proc] gets disturbed by the asterisk (and returns a number of proc names that start with tcl), other subcommands like [info args] don't do glob matching, so replacing the "proc" case above with

proc {![catch {info args $name}]}

fixes the problem. Thank you! (RS)

CL responds with a couple of observations:

1. This is minor stuff. There are applications in production that have far bigger robustness problems, yet have been satisfying end-users for years. I can't even bring myself to call this a problem--it's more of a footnote.

2. On the other hand, it interests and entertains me how often programs admit minor transformations that are strictly superior in the sense that they're as readable and succinct, while being "more correct" (satisfying over a wider input space, for example). I salute Richard for so quickly finding such a transformation in this case.


LV 2008-06-26: command'type2 has a requirement that Tk be loaded before you use it. Judious use of catch probably could handle the issue...

Lars H: Another problem is that alias names aren't normalized — interp alias faithfully records exactly the string used when creating the alias, but the $name used for the command can be very different. For example, if you do

foreach ns {:: bar ::baz} {
    interp alias {} ${ns}::foo {} ::foopkg::dispatch $ns {some more data}
}

then interp aliases will faithfully return

::::foo bar::foo ::baz::foo

and it is only these strings you can use with interp alias if you want to look up what the alias points to.


SS 2006-06-30: schlenk just produced this procedure to check if a given command exists, it is in theory trivial but in pratice to get it right is not too simple, so maybe it's a good idea to put the proc here:

proc cmd_exists cmd {
    string equal {} [namespace eval :: [list namespace which $cmd]]
}

This above checks if the command is fully qualified basically (callable from the global namespace with the given name), and could be used for a callback. If you just want to see the fully qualified name, use a simple namespace which.


Can't you just use info?

$ tclsh
% info command info
info
% info command nonsense
%

schlenk: Not if you have funny command names that are glob patterns like a proc named * or info*, as info command does not have an -exact switch.

LV: well, technically you can:

$ tclsh
% proc info* {args} {return}
% info command info*
info info*
% info command {info*}
info info*

However, the info command returns _all_ the matching names in this case, so you have to be ready to deal with that. That's a bit annoying.

Lars H: It is perfectly possible to do an exact search by escaping all glob metacharacters in the string, e.g.

proc cmd_exists_2 {cmd} {
    llength [info commands [string map {\\ \\\\ * \\* ? \\? [ \\[ ] \\]} $cmd]]
}

Then whether this would be any faster than the namespace which form is another matter... I suspect it could be that they don't do exactly the same thing when it comes to not-fully-qualified names, and that depending on the problem one might want one or the other.

escargo 2005-07-01: Maybe there is some argument for making all the commands that use glob matching by default also accept -exact (and maybe even -regexp). Using glob matching by default is a convenience, but more specific behavior should be allowed.

Making commands more uniform (by ensuring uniform matching behavior) seems like it would make the language more regular and easier to understand.

Lars H: Well, that sounds like a short summary of TIP 246 .

NEM: You can always use either:

expr {$cmd in [info commands]}
lsearch -exact [info commands] $cmd

DKF 2012-10-20: I'm working on a project to identify what commands should be bytecode-compiled (and what are and aren't).

See Also

Tcl commands
command prefix
command variety