Updated 2018-03-28 16:16:07 by pooryorick

proc, a built-in Tcl command, creates a new command.

Synopsis  edit

proc name arguments body

Documentation  edit

official reference

See Also  edit

apply
command
corp
Generate a script to reproduce a procedure.
Creating Commands
Enumerates and compares the commands which create commands:
lego proc manipulation
A sort of proc templating facility.
Local procedures
A discussion on limiting the lifespan of a proc inside another procedure.
lproc
Like proc but the body is a list of lists.
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
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 commands

Description  edit

proc creates a new Tcl procedure named name, replacing any existing command having the same name. Whenever the new command is evaluated, the contents of body are evaluated in the namespace where the procedure is defined, at a new level where the only accessible variables are those created to hold the arguments passed to the procedure. global, upvar, namespace upvar, and variable can be used to link in namespace variables and variables at other levels. Similarly, uplevel and namespace eval can be used to evaluate scripts in namespaces or at other levels. Command names in body are resolved as described in name resolution.

When name is unqualified (does not include the names of any containing namespaces), the new command is created in the current namespace. If name includes any namespace qualifiers, the command is created in the specified namespace. A partially-qualified name is resolved relative to the current namespace.

arguments is a list, possibly empty, of arguments to the procedure. If any list item itself contains two items, the second item becomes the default value for that argument. When the command is evaluated, each actual argument is stored in the variable named by the argument.

Any argument that follows an argument having a default value must itself have a default value.

A procedure may accept a variable number of arguments by naming the last argument args:
proc myvarproc {foo bar args} {
    puts "<$foo> <$bar> <$args>"
}

myvarproc a b c d e
# => <a> <b> <c d e>
myvarproc a b
# => <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 command name. Thus, list can be implemented as:
proc List args {set args}

An argument named args only has this special behaviour if it is the last argument.

The value of a procedure is the value of the last command evaluated in body . return can be used to end a procedure at some point other than the last command in body.

When a procedure is renamed, any existing procedure by that name is overwritten, and 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 by the interpreter, a command created by proc does not extend Tcl.

Procedure Results  edit

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 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

DKF: Use lassign from 8.5 onwards.
lassign [makeThreeResults $input] first second third

Procs Always Bind to a Namespace  edit

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  edit

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  edit

proc foo {args} {
    puts "proc = [lindex [info level 0] 0]"
}

See: info level

Clobbering Existing Procedures  edit

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  edit

bytecode
the main page on the subject
Proc to bytecodes: when, how does it happen

The Empty Name  edit

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 ::.

Illegal Names  edit

As CMcC noted in the Tcl Chatroom, 2014-11-28, the name of a procedure in any namespace other than the global namespace may not begin with :. This is a consequence of designating :: as the namespace delimiter, which is considered an unfortunate development by some Tcl programmers who would have rather seen lists used for that purpose.

Pass by Reference  edit

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  edit

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  edit

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