'''[http://www.tcl.tk/man/tcl/TclCmd/proc.htm%|%proc]''', a [Tcl Commands%|%built-in] [procedure], creates a new [procedure]. ** Synopsis ** : '''proc''' ''name arguments body'' ** Documentation ** [http://www.tcl.tk/man/tcl/TclCmd/proc.htm%|%official reference]: ** See Also ** [apply]: [command]: [corp]: Generate a script to reproduce a procedure. [Creating Commands]: Enumerates and compares the procedures that create procedures. [lego proc manipulation]: A sort of procedure templating facility. [Local procedures]: A discussion on limiting the lifespan of a procedure inside another procedure. [lproc]: Like `proc` but the body is a list of lists. [overloading proc]: [Guarded proc]: [Lambda in Tcl]: Unnamed/anonymous procedures. [Steps towards functional programming]: [Jimulation]: Overloads [proc] to accept a list of static variables. [Printing proc sequence]: A mechanism to trace procedure calls. [Procedure calling timed]: Timing procedure calls. [proc alias]: How to make an alias for a procedure [Procs as data structures]: [Procs as objects]: [Runtime creation of procs]: [scope]: [Simple proc tracing]: [mkproc]: [If we had no proc]: [saving all procedures]: [Wrapping a procedure]: Wrap a procedure and call the original, which is stored in `$next`, at any time [wrapping commands]: Various approaches to wrapping procedures. ** Description ** '''`proc`''' creates a new [procedure] named ''name'', replacing any existing procedure having the same name. When ''name'' is not absolute, i.e. when the first [namespace] part is not the empty string, the new procedure is created in the current namespace. If ''name'' includes any namespace qualifiers, it is [namespace%|%resolved] relative to the [namespace current%|%current namespace] and the procedure is created in the corresponding namespace. ''arguments'' is a list of zero or more argument specifications. Each specification where the first item is the name of the variable to store the corresponding argument to, and the second item, if provided, is the default value of the variable. Any ''argument'' that follows an ''argument'' having a default value must itself have a default value. When the new procedure is called, each argument to the call is stored in the specified variable at the new [level] for the call, and ''body'' is [eval%|%evaluated] at that level. `[global]`, `[upvar]`, `[namespace upvar]`, and `[variable]` can be used to link local variables to namespace variables and variables at higher levels. Similarly, `[uplevel]` and `[namespace eval]` can be used to evaluate scripts in namespaces or at other levels. Procedure names in ''body'' are resolved as described in [namespace%|%name resolution]. '''Example:''' ====== proc myproc {arg1 arg2} { # do something here, e.g. puts [list arg1 $arg1 arg2 $arg2] } ====== If the last argument specification for a procedure is '''`[args]`''', then the procedure accepts a variable number of arguments, which are stored as a list in `$args`: ====== proc myvarproc {foo bar args} { puts "<$foo> <$bar> <$args>" } myvarproc a b c d e # => myvarproc a b # => <> ====== When the command is evaluated all additional words are added to a list which is then assigned to `$args`. If there are no additional words, $args is empty. If `args` is the only ''argument'', `$args` is a list of all the words after the procedure name. Thus, `[list]` can be implemented as: ====== proc List args {set args} ====== The value of a procedure is the value of the last command evaluated in ''body'' . `[return]` can be used to end a procedure at an earlier point in ''body''. When a procedure is overwritten no warning is given. To guard against such an event, see [overloading proc] with a [Guarded proc]. Because ''body'' is a script to be evaluated, a procedure created by `[proc]` does not [extension%|%extend] Tcl. ** Procedure Results ** [Silas] 2005-08-18: 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 value of the procedure, and the others using [upvar%|%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 [upvar%|%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 ====== [DKF]: Use [lassign] from 8.5 onwards. ====== lassign [makeThreeResults $input] first second third ====== ** A Procedure Always Has a Name ** When a procedure is created from within the body of another procedure, it is bound to the given name in a namespace, remains, even after evaluation at the current level has completed, until it is deleted or its namespace is deleted. There is no way to create a "local" procedure at the current [level]. ====== namespace eval foo { proc one {} { puts -nonewline {Eat } proc two {} { puts -nonewline {more } proc three {} { puts chicken. } } } } foo::one foo::two foo::three ====== ** Default Values ** [http://www.changhai.org/articles/technology/programming/tcltricks.php%|%Selected Topics in Tcl/Tk], Changhai Lu: A discussion of the default argument feature. ---- [schlenk] 2004-08-03: Here's a little procedure 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? {} { puts [list hey [info level -1]] 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 ====== [HaO] 2011-03-16: The above is very good. I have seen an in-procedure variant like that: ====== proc c {a {b {}}} { if {2 == [llength [info level 0]]} { # code when optional argument b was not passed } } ====== ---- Doing nonsensical things with default args: ====== proc defaultargs {{def a} undef} { puts [format {def: "%s", undef: "%s"} $def $undef] } defaultargs x y ;# -> def: "x", undef: "y" catch {defaultargs x} msg puts $msg ;#-> wrong # args: should be "defaultargs ?def? undef" ====== ** Determine the Name of the Current Procedure ** ====== proc foo args { puts "proc = [lindex [info level 0] 0]" } ====== See: [info level] ** Clobbering Existing Procedures ** When creating a new procedure, no warning is given when it replaces an existing procedure by the same name. If, for example, in a Tk applicaiton, you were to code: ====== proc . {} {} ====== you would find that you have destroyed the default toplevel, likely causing the app to exit. This is because for each widget, there exists a procedure whose name is the path of the widget. Defining a proc having the name of any existing widget has the potential in the very best cases to result in strange behavior and, and in worse cases to be catastrophic. ** Procedure Compilation ** [bytecode]: The main page on the subject. [Proc to bytecodes: when, how does it happen]: ** The Empty Name ** [AMG] [PYK]: The name of a procedure can even be the empty string, `{}`, but this has a weird interaction with `[rename]` and the way we abuse it for deleting procs. ====== proc {} {} { puts "my name is [info level 0]" } {} ;# -> my name is {} rename {} {} catch {{}} msg einfo puts hello puts $msg ;# -> invalid command name "" ====== Also strange: you can create a proc named by the empty string, `{}`, but you can't use [rename] to move it. [DKF]: You can, but only by renaming to a fully-qualified name, like `::`. ** Illegal Names ** Except to separate namespace parts the namespace separator string, `::`, cannot occur in the name of a procedure. This is considered an unfortunate development by some Tcl programmers: If the notation for a procedure name was a list, it would have remained possible to give a procedure any name at all. See also [CMcC%|%CMcC's] comments in the [Tcl Chatroom], 2014-11-28. ** Pass by Reference ** [Implicit upvar]: [RS]'s take on the matter [deref]: [Larry Smith]'s approach [DKF] 2003-07-16 [PYK] 2013-09-01: 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 first * $a] == 0} { append header "upvar 1 \[set $a] [string range $a 1 end];" } } proc $name $arglist $header$body } proc2 test {a *b c} {puts "a=$a, b=$b c=$c"} set quantity 4 test 1 quantity 3 ;# -> a=1, b=4, c=3 ====== ** Naming Hacks ** Any string is potentially valid proc name. [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]! ---- A procedure name that starts with a hash character `#` can be called by somehow ensuring the `#` isn't the first character: ====== proc #test {} {puts [lindex [info level 0] 0]} \#test ;# -> #test {#test} ;# -> #test \x23test;# -> #test [namespace current]::#test ;# -> ::::#test ====== Remember that comments are detected prior to the evaluation of a script. `#` has no importance when commands are being ''evaluated''. ** Misc ** ''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.'' : - Charles Babbage, 1864 <> Arts and crafts of Tcl-Tk programming | Tcl syntax | Command | Tcl | Data Structure