'''[http://www.tcl.tk/man/tcl/TclCmd/proc.htm%|%proc]''', a [Tcl Commands%|%built-in] [Tcl] [command], creates a new command. ** 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 proc [Local procedures]: a discussion on limiting the lifespan of a proc inside another proc [overloading proc]: [Guarded proc]: [Lambda in Tcl]: un-named/anonymous procedures [Steps towards functional programming]: [Jimulation]: an overloaded [proc] that also takes a set of static variables [Printing proc sequence]: a mechanism to trace what procs are called [Procedure calling timed]: timing proc calls [proc alias]: how to make an alias for a procedure [Procs as data structures]: [Procs as objects]: [Runtime creation of procs]: [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 commands ** Description ** 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 evaluated in a new [info level%|%level] by the Tcl interpreter. When ''name'' is unqualified (does not include the names of any containing namespaces), the new procedure is created in the current [namespace]. If name includes any namespace qualifiers, the procedure is created in the specified namespace. A partially-qualified name is resolved relative to the current namespace. ''arguments'' is a list, possibly empty, of formal arguments to the procedure. If any list item itself contains two items, the second item becomes the default value for that argument. When the procedure is invoked, each actual argument will be stored in the variable named by the formal argument. After the first default value to a formal argument is encountered, all additional formal arguments must have default values. If the name of the last formal argument is the literal value, "`args`", it is treated in a special way: When the procedure is invoked, `$args` becomes a list of any additional values that were passed to the procedure beyond what was specified in the formal argument. I.e., a variable number of arguments may be passed to such a procedure. The name "args", is not special in any other position in the list of formal arguments. The value of a procedure is the value of the last command executed in the procedure. [Return] causes the procedure to exit immediately. When a procedure is [rename%|%renamed], any existing procedure by that name will be overwritten, and no warning will be given. To guard against such an event, see [overloading proc] with a [Guarded proc]. ** 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 return value of the proc, and the others using [upvar]ed 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]ed 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 ====== ** Procs Always Bind to a Namespace ** Even when they are created from with the body of another function, procs are bound to the current namespace, and exist for the life of that namespace, or until they are deleted: ====== 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 a discussion of Tcl proc's default argument capability. ---- [schlenk] 2004-08-03: 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? {} { puts "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-proc 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 Proc ** ====== proc foo {args} { puts "proc = [lindex [info level 0] 0]" } ====== See: [info level] ** Clobbering Existing Procedures ** The name of a procedure may be any string, including the empty string. 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 each widget, there exists a command whose name is the path of the widget. 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, catastrophe. ** Procedure Compilation ** [bytecode]: the main page on the subject [Proc to bytecodes: when, how does it happen]: ** The Empty Name ** [AMG] [PYK]: The proc name 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 "" but you can't use [rename] to do it. [DKF]: You can, but only by renaming to a fully-qualified name, like `::`. ** 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 proc 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