[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 ---- [KPV] Nice, but I like to abbreviate my options. So here's a slightly tweaked version that allows you to use shorter versions of options names (as long as they're unique). It also uses [DGP]'s better error handling if possible. proc named {args defaults} { upvar 1 "" "" array set "" $defaults set aname [lsort [array names {}]] foreach {key value} $args { if {! [info exists ($key)]} { ;# Check for unique prefix set possible {} foreach keyname $aname { if {[string first $key $keyname] == 0} { lappend possible $keyname } } if {[llength $possible] != 1} { ;# Not a unique prefix set emsg [expr {[llength $possible] ? "ambiguous" : "bad"}] append emsg " option \"$key\": must be [join $aname {, }]" regsub {(.*),} $emsg {\1 or} emsg if {$::tcl_version >= 8.5} { return -code error -level 2 $emsg } error $emsg } set key $possible } set ($key) $value } } ---- '''[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} { defargs -from 0 -to end -with "" string replace $s $(-from) $(-to) $(-with) } Note that it's quite nice now, but not the same as Python's idiom, which lets you mix required, optional, default, and named args. Required args etc can also be passed as named args. And named args can also be new ones, not listed in the definition. ---- [procargs] is a proc that I wrote some time ago that allows one off switchified procs, existing procs to be switchified and the overloading of proc itself. I believe that it addresses the above complaints about not being as general as python (but I know nothing about python). It defines the defaults in the usual proc way and extracts the switch names and values using tcl's [info] introspection [JBR]. procargs replace { str { from 0 } { to end } { with {} } { string replace $str $from $to $with } I like this better since it overloads the existing proc API. It is easy to overload proc, source a package and call procs in the package with named args. ''Can you give an example of use? Does procargs affect error [traceback]s? -[jcw]'' Hmmm... I thought that the above was an example of its use. The code there creates a proc named replace that can be called as the other replace procs on this page: puts [replace abcdefg -from 3 -to 4 -with xx] abcxxfg There was a small typo in the error handling which I fixed, and an error in switch specification reports a coherent error message but adds a level to the error stack. ---- [Larry Smith] Take a look at [init]. Small and simple, but powerful. ---- [Arts and crafts of Tcl-Tk programming] | [Category Argument Processing] ]