Version 49 of proc

Updated 2005-08-26 22:53:35

proc - Create a Tcl procedure

 proc name args body  

http://www.tcl.tk/man/tcl/TclCmd/proc.htm

The proc command creates a new Tcl procedure named name, replacing any existing command or procedure there may have been by that name. Whenever the new command is invoked, the contents of body will be executed by the Tcl interpreter. Normally, name is unqualified (does not include the names of any containing namespaces), and the new procedure is created in the current namespace. If name includes any namespace qualifiers, the procedure is created in the specified namespace. Args specifies the formal arguments to the procedure. It consists of a list, possibly empty, each of whose elements specifies one argument. Each argument specifier is also a list with either one or two fields. If there is only a single field in the specifier then it is the name of the argument; if there are two fields, then the first is the argument name and the second is its default value. If the procedure doesn't execute an explicit return, then its return value is the value of the last command executed in the procedure's body.(From: Tcl Help)


Redefining an existing proc occurs silently and may result in unsuspected errors (or even security breaches) if the proc is not carefully constructed. You can detect this type of error by overloading proc with a Guarded proc.


For a discussion on limiting the lifespan of a proc inside another proc, see Local procedures


For unnamed/anonymous procedures see Lambda in Tcl and Steps towards functional programming.


Order of arguments: It is not specifically documented in the man page, but a proc's arguments must be defined to occur in the following sequence:

  • zero or more undefaulted arguments
  • zero or more defaulted arguments
  • optionally the special literal args argument; its appearance as the last argument results in the possible collection of additional arguments into a list called args.

While you may name any proc argument args, it won't be initialized with the special accumulating effect unless it is the last argument variable in the proc's argument list.

Note With Tcl 8.4 you can do silly things with defaulted args:

    proc defaultargs {{def a} undef} \
    {
      puts "def \"$def\", undef \"$undef\"" 
    }

    defaultargs x y
  ->  
  def "x", undef "y"

    catch {defaultargs x} msg
    puts $msg
  ->
  wrong # args: should be "defaultargs ?def? undef"

See http://www.changhai.org/contents/technology/programming/tcltricks.html for a discussion of Tcl proc's default argument capability.


See Proc to bytecodes: when, how does it happen. Printing proc sequence implements a mechanism to trace what procs are called. Procedure calling timed discusses timing proc calls.


Note that name may be any string. However, note that due to the silent replacment mentioned above, if in a Tk application you were to code:

 proc . {} {}

you will find that you have destroyed the default toplevel, mostly causing the app to exit.

This is because each widget created has a proc-equivalent (with the name of the widget) created. Defining a proc of the name of any existing widget is going to result (in the very best of cases) potentially strange behavior and, in worse cases, catestrophic cases.


RS "Any string" includes things that look like array elements (but aren't), with which you can emulate "arrays of function pointers":

 % proc f(1) {} {puts hello}
 % proc f(2) {} {puts world}
 % proc f(3) {} {puts again}
 % for {set i 1} {$i<=3} {incr i} {f($i)}
 hello
 world
 again

And a certain introspection is possible too:

 info proc f(*) => f(1) f(2) f(3)

Update 2002-11-15: You don't have to stop at simulating - you can just have arrays of function pointers!

MG You can also define procs with names starting with the hash character (#) which makes them completely uncallable, as the Tcl parser evaluates it as a comment rather than a command. Whether or not there'd be any reason to do so (aside from "commenting out" a particular proc quickly), I'm not really sure, but I thought it was an interesting quirk.

Lars H: No, a name starting with a # doesn't render a procedure impossible to call. Consider

 % proc #test {} {info level 1}
 % \#test
 #test
 % {#test}
 #test

Remember that comments are detected before a command is split into words. # has no importance when commands are being evaluated.

MG wasn't aware you could do that - I guess my understanding of how and when comments were checked for was a little off. Will have to read into that some more in the future... :)


DKF - 16-Jul-2003 - Here's a way to do magic "dereferencing" which C hackers might like...

 proc proc2 {name arglist body} {
    set header {}
    foreach a $arglist {
       if {[string match {\**} $a]} {
          append header "upvar 1 \${$a} [string range $a 1 end];"
       }
    }
    proc $name $arglist $header$body
 }

Trying it out:

 % proc2 test {a *b c} {puts "a=$a, b=$b c=$c"}
 % test 1 argc 3
 a=1, b=0 c=3

See also Implicit upvar for RS's take, deref for Larry Smith's.


schlenk - 03-Aug-2004 - Here's a little proc to check if defaults were used or actual arguments were present, this can be handy if a default value is used as a don't care token.

 proc defaultvalues? {} {
  expr {[llength [info args [lindex [info level 1] 0]]] - 
  ([llength [info level 1]]-1)}
 }

Trying it out:

 % proc test {a {b 1} {c 2}} {puts [defaultvalues?]}
 % test 1
 2
 % test 1 2
 1
 % test 1 2 3
 0

See Jimulation for an overloaded proc that also takes a set of static variables.


Silas - 18-Aug-2005 - I think the best way to return multiple values is to use a list. For example:

 proc v {} {
   set value1 "somevalue"
   set value2 "anothervalue"
   return [list $value1 $value2]
 }

arjen told me would be a good idea to use upvar. See (I haven't tested it):

 proc v {name1 name2} {
   upvar 1 $name1 n1
   upvar 1 $name2 n2

   set n1 1
   set n2 2
 }

 v N1 N2
 puts "$N1 $N2"

RS: The first approach, returning a list of the results, is "cleaner" in the functional programming sense. The upvar approach creates side effects as it changes the value of variables.

Lars H: In real life, which approach is preferable varies quite a lot. There are on the whole at least three possibilities to consider:

  • Return the list of the results.
  • Return one result as the return value of the proc, and the others using upvared variables.
  • Return all results in variables.

Taking apart a list of values returned is sometimes annoyingly cumbersome, but in other cases that list is a good thing to have. Usually it all depends on what you're going to do with the results once you've got them.

One thing that is fairly easy to do using upvared variables is to provide optional results -- provide an optional argument for the name of a variable in which to pass back a result, but if such an argument is not given then don't pass the result back (perhaps avoid computing it altogether).

RS: Taking apart a list is pretty easy with the foreach ... break idiom :)

 foreach {first second third} [makeThreeResults $input] break

Larry Smith I still think let is more readable:

  let a b c @= 1 2 3

willdye Here's a quick demo of how procs are (normally) declared in the global scope. Feel free to extend this code to also show how namespaces can make procs 'local'.

 % proc outer {} {puts "If a proc is declared inside another proc, is it local?"
     proc middle {} {puts "No. Unlike vars, proc declaration is normally global."
         proc inner {} {puts "If you want a local proc, just use a namespace."}}}
 % outer
 If a proc is declared inside another proc, is it local?
 % middle
 No. Unlike vars, proc declaration is normally global.
 % inner
 If you want a local proc, just use a namespace.
 % 

Charles Babbage, 1864: Every set of cards made for any formula will at any future time recalculate that formula with whatever constants may be required. Thus the Analytical Engine will possess a library of its own.


Procs as data structures - Procs as objects - Runtime creation of procs - Simple proc tracing


[ Arts and crafts of Tcl-Tk programming | Tcl syntax help | Category Command (as a part of Tcl) | Category Data Structure | ]