Version 14 of Named arguments

Updated 2004-01-15 23:25:59

Richard Suchenwirth 2004-01-15 - Arguments to commands are mostly by position. But it's very easy to add the behavior known from Python or Ada (?), that arguments can be named in function calls, which documents the code a bit better, and allows any order of arguments.

The idea (as found in Welch's book) is to use an array (here called opt) keyed by argument names. Initially, you can set some default values, and possibly override them with the args of the proc (which has to be paired, i.e. contain an even number of elements):

 proc replace {s args} {
   array set opt [concat {-from 0 -to end -with ""} $args]
   string replace $s $opt(-from) $opt(-to) $opt(-with)
 }

#--- Testing:

 % replace abcdefg -from 3 -to 4 -with xx
 abcxxfg

Keywords can come in any order or be omitted (in which case the defaults apply). Flaw: "Undefined" keywords, e.g. typos (-wiht), are not detected...


Here's one which catches (but does not identify) typo's -jcw

 proc replace {s args} {
   array set opt {-from 0 -to end -with ""}
   set n [array size opt]
   array set opt $args
   if {$n != [array size opt]} { error "unknown option(s)" }
   string replace $s $opt(-from) $opt(-to) $opt(-with)
 }

RS feels prompted to reply with an error-identifying version, changed to using the "anonymous array" "" instead of opt, which makes access to such arguments look better:

 proc replace {s args} {
   array set "" {-from 0 -to end -with ""}
   foreach {key value} $args {
      if {![info exists ($key)]} {error "bad option '$key'"}
      set ($key) $value
   }
   string replace $s $(-from) $(-to) $(-with)
 }

As the boilerplate is getting a bit lengthy, yet looks like it might be used more than once, here it is factored out (and with improved error message):

 proc named {args defaults} {
    upvar 1 "" ""
    array set "" $defaults
    foreach {key value} $args {
      if {![info exists ($key)]} {
         error "bad option '$key', should be one of: [lsort [array names {}]]"
      }
      set ($key) $value
    }
 }

#--- The use case now looks pretty slick again:

 proc replace {s args} {
   named $args {-from 0 -to end -with ""}
   string replace $s $(-from) $(-to) $(-with)
 }

#--- Testing:

 % replace suchenwirth -from 4 -to 6 -with xx
 suchxxirth
 % replace suchenwirth -from 4 -to 6 -witha xx
 bad option '-witha', should be one of: -from -to -with

DGP Can be a little slicker using a Tcl 8.5 feature. Where you have

         error "bad option '$key', should be one of: [lsort [array names {}]]"

in [named], replace it with

         return -code error -level 2 "bad option '$key', should be one of: [lsort [array names {}]]"

for a cleaner stack trace.


jcw - Neat. FWIW, my personal usage style pref would be:

 proc replace {s args} {
   defaults -from 0 -to end -with ""
   string replace $s $(-from) $(-to) $(-with)
 }

Arts and crafts of Tcl-Tk programming