Version 29 of args

Updated 2018-01-24 00:04:43 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

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.


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.