Version 79 of proc

Updated 2014-11-27 15:43:04 by pooryorick

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

Synopsis

proc name arguments body

Documentation

official reference

See Also

apply
command
corp
generate a script to reproduce a proc
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 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
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

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

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

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