extending the notation of proc args

How to quickly write TCL procs with switches and the like?

This is a proposal on how to extend the notation of args in the definition of proc. It is a request for comments, no implementation is available now.

What types of args / switches shall be available?

  1. switches with values, e.g. -start index of lsearch; they do have a default value
  2. switches without value, e.g. -exact of lsearch; their existence is boolean, false by default
  3. required named parameters
  4. optional named parameters with default values
  5. unnamed optional parameters

How shall these parameters be accessed inside the procedure?

Numbered as above...

  1. switches could be just local variables, e.g. ${-start}
  2. boolean values of local variables, e.g. ${-exact}
  3. this is the default
  4. just like the original implementation
  5. args is still available to catch the rest

Suggestion

Allow extended syntax of args: This special parameter name comes as a list...

  • first item is the keyword args
  • items with leading dash are boolean switches (with (1.) or without (2.) default values)
  • items without leading dash are named parameters (with (3.) or without (4.) default values)
  • items that are a list of two elements are the name and the default value respectively
  • the last item can optionally be args again (5.)

Example

Assume an extension to subst:

proc mysubst {{args -nobackslashes -nocommands -novariables -nocomplain {-uplevel 0} -inplace string1 {string2 {}} args}} {
   # the above notation is backward compatible ("default values" of args are currently ignored).
   # a redirection of proc could generate the following code:
   set -nobackslashes false
   set -nocommands false
   set -novariables false
   set -nocomplain false
   set -uplevel 0
   set -inplace false
   set string2 {}
   for {set i 0} {$i < 7} {incr i} {
      # 7 is the max number of switches plus their values
      set c [lindex $args $i]
      if {$c in {-uplevel}} {
         # 1. switches with default values
         set $c [lindex $args [incr i]]
      } elseif {$c in {-nobackslashes -nocommands -novariables -nocomplain -inplace}} {
         # 2. boolean switches
         set $c true
      } else {
         if {$c eq {--}} {incr i}
         break
      }
   }
   incr i -1 ;# or do not incr in the first line below (which is harder to generate)?
   # 3. required (consider errors!)
   set string1 [lindex $args [incr i]]
   # 4. optional
   if {[llength $args] > [incr i]} {set string2 [lindex $args $i]}
   # more of the above line can be nested in the then-branch...
   # 5. unnamed
   set args [lrange $args $i end]
   unset i c
   # end of generated code - body below

   # relative levels are offset by one
   catch {incr -uplevel}

   # having both string and args is just to demonstrate the possibilities - use args only
   if {{} eq $string2} {set args $string1} {set args [list $string1 $string2 {*}$args]}

   # TODO implement
   # something like the following - use catch if -nocomplain and return unmodified
   # also, if -inplace use upvar 1 and modify the values; plus consider a loop for args...
   # return [uplevel ${-uplevel} [list ::subst {*}$forward_switches $string]]

   # demo output
   foreach var {-nobackslashes -nocommands -novariables -nocomplain -uplevel -inplace string1 string2 args} {
      puts "$var is {[set $var]}"
   }
}
set x 14
set y {$x}
set z {[expr $x * 3]}
subst -nocomplain -inplace x y z

Considerations

  • the double-dash "--" is always a good idea to protect non-switches starting with a dash
  • implementation could happen as proc-redirection or even better natively (just like the interpreter handles optional parameters now!)
  • should the generated code be visible in info body?
  • the above generated code does not allow parameters with names c or i.
  • parameters are switches if and only if their name starts with a dash

See also

  • [argparse] implements many advanced switch and parameter processing capabilities