Updated 2018-01-24 13:53:55 by dzach

Special name for an argument to a proc or lambda term - if it's last in the argument list, it will contain a list (possibly empty) of all the remaining arguments. The use of a magical args parameter is common in many other procedure-like methods (e.g. those created by various OO extensions).
 proc demo {first {second "none"} args} {
        puts "first = $first"
        puts "second = $second"
        puts "args = $args"
 }

 demo one
 demo one two
 demo one two three four five

results in:
  first = one
  second = none
  args = 
  first = one
  second = two
  args = 
  first = one
  second = two
  args = three four five

example args (using XOTcl)
   Class Club -parameter {id {name unknown}}
   Class Player -superclass Club -parameter {{name unknown} {position unknown}}

   Player proc show_players args {
     if { [string length $args] == 0} {
       puts "Show players DB"
       foreach p [my info instances] {
         puts "[$p name] [$p position]"
       }
       return
     }

     set pos [string toupper $args]
     puts "Players with postition $pos:"
     foreach p [my info instances] {
       foreach char [split $pos {}] {
         if {[string first $char [$p position]] == 1} {
           puts "[$p name] [$p position]"
           break
         }
       }
     }
   }

Is it just me, or is that a pretty poor example of args? You do [string length $args] and [string toupper $args] but you firmly established that args is a list, not a string. It is generally accepted as bad practice to perform string operations on lists.

LES But, if everything is a string, aren't lists strings too? On the other hand, there has been quite some debate on whether everything is a list rather than a string...

LV I don't know that I would go so far as saying that it is bad practice to perform string operations on lists.

I would say that it is generally bad practice to perform list operations on variables known only to have strings. And I would say that some string operations on lists may result in results that , at first blush, one might not expect.

Lars H: The problem with applying string operations on known pure lists like $args is basically shimmering. Applying a string operation will require generation of a string representation, and the cost for that (in memory and processing time) is often better avoided whenever possible. If you pass multi-megabyte lists around (something Tcl 8 handles beautifully well), you probably don't want to double the memory footprint by also generating their string representations.

An extreme example:
  set val x
  for {set n 1} {$n<=64} {incr n} {set val [list $val $val]}
  puts "Tcl will get this far."; flush stdout
  string length $val
  puts "But it runs out of memory before it gets this far.!"

LV So then the example isn't a poor example of args - but a poor example of good performance.

MG offers a quick example off the top of his head (and therefore untested), on his way through...
proc randomCmd {args} {

   set error {wrong # args: should be "randomCmd ?-arg value ...? string"}
   set num [llength $args]
   if { $num == "0" || ($num%2) == 0 } {
      error $error;
   }
   array set opts [list -width 5 -height 5 -fg [list] -bg [list] -foreground blue -background red]
   if { $num > 1 } {
      foreach {x y} [lrange $args 0 end-1] {
         if { ![info exists opts($x)] } {
            error "unknown option \"$x\"";
         }
         set opts($x) $y
      }
   }
   foreach {x y} [list -fg -foreground -bg -background] {
      if { [llength $opts($x)] > 0 } {
         set opts($y) $opts($x)
      }
      unset opts($x)
   }

   echo "String is \"[lindex $args end]\". Apply these options: [array get opts]"
}

LV Here's where I am trying to use args - maybe someone has a suggestion. My code is this:
 % proc d {name args} {
        puts "DEBUG: $name: $args"
 }

 % d mainline "this is the first spot in the mainline"
 DEBUG: mainline: {this is the first spot in the mainline}

That is to say, it is intended as a place to control debug statements. I can just return rather than do the puts, to turn off the debugging.

HOWEVER, the debugging output looks peculiar. Not a problem - it is just for debugging. But if I were going to use this for something else, I wouldn't want the extra {} in there.

RS: try join $args to get rid of braces :)

dzach 2018-01-23: args seems not to follow the rule the other arguments do:
% proc test {a {b 1} {args 2}} {
    puts "$a $b $args"
}
% test 0
0 1  ; # <- no default value for 'args'

Is there a reason for this, or I just hit a bug?

I would expect something like this (workaround):
% proc test {a {b 1} {args 2}} {
    info default [lindex [info level [info level]] 0] args arg
    if {$args eq {}} {
        set args $arg
    }
    puts "$a $b $args"
}
% test 0
0 1 2

foo: args doesn't seem to follow the same rules because it is never valueless and thus never eligible to receive a default value. Its value is the (possibly empty) list of arguments that overmatch the named parameters, and it always gets that value.

dzach I still cannot see what it would hurt to have args follow the rule, other than adding a few microseconds to the execution time. The docs state "[the argument's] value will be the value of corresponding argument in the invoking command or the argument's default value". Why break a rule when it costs nothing to follow it? I bet, changing it wouldn't break a single proc in the tcl universe.

foo: it does follow the rule. The rule says that there must be exactly as many actual arguments in the invocation as formal parameters in the procedure declaration, with two variations: 1) there can be fewer arguments if the difference is made up with defaulted parameters, and 2) there may be more arguments if the last formal parameter is called args, in which case that parameter is assigned the list of excess arguments. Allowing a default value for args probably wouldn't cause much trouble, but it would be confusing (as it mixes up two different mechanisms) and unnecessary (since every practical use of it can be served by adding another parameter with a default value, or by the usual idiom of checking the length of args and taking the appropriate action).

dzach Yes, its not a big deal to add a parameter with the default value, or to initialize args with the desired default value inside the proc, current behavior just surprises me as unexpected. It seems to me a result like with the workaround above, which does not enforce an empty args local variable despite argument args having a default value, as does the current implementation, wouldn't be confusing; from a programmer's point of view, it follows all rules as (well, I) expected, allowing both default and collect the excess arguments functionalities. And if the programmer doesn't set a default args argument, there is no change from current behavior:
% test 0 ; # 'args' set without a default value in the declaration of proc 'test'
0 1
% test 0 a
0 a
% test 0 a b c
0 a b c

% test 0 ; # 'args' set with a default value in the declaration of proc 'test'
0 1 2
% test 0 a
0 a 2
% test 0 a b c
0 a b c

From a user's point of view, there is absolutely no difference, since the user will be getting the end result of the proc in any case, whether args was set with a default value or inside the proc. There might, however, be a mix up of mechanisms from a tcl maintainer's point of view, judging from KPV's comment below, about Tip 288.

See also magic names.

KPV: I always liked Tip 288 -- Allow "args" Anywhere in Procedure Formal Arguments.

I think it would be really useful in handling optional arguments which come before required arguments. But seeing how it's over 11 years old I doubt it will ever be implemented.