?set

GPS Oct 16, 2003 - It occurred to me that I might accidentally set the wrong variable (it has happened in the past), and it could cause me to have a nasty debug session, so I decided to come up with a proc that sets a variable but returns an error if it doesn't exist.


 proc ?set {v_name val} {
  upvar $v_name v
  if {![info exists v]} {
   return -code error "expected $v_name to exist!"
  }
  if {[array exists v]} {
   return -code error "can't set \"$v_name\": variable is array"
  }
  set v $val
 } ;# GPS

Here is a log demonstrating usage:

 % ?set foo bar
 expected foo to exist!
 % set foo abc
 abc
 % ?set foo bar
 bar
 % set foo
 bar
 % 

See also ?append,?lappend


KBK has used the name, ?set, to mean something rather different:

    proc ?set { v_name val } {
        upvar $v_name v
        if { ![info exists v] || $v != $val } {
            set v $val
        }
        return $v
    }

The effect of this procedure is to set the given variable if and only if it does not already have the desired value. This behaviour is useful in the cases where there are networks of write traces on variables; setting a variable may have a cascade of effects as the traces fire. It's sometimes desirable to take these effects only when the value actually changes. (It's perhaps cleaner to have the trace callback monitor the old values; that does, however, get trickier because it needs to maintain its own backing copies.)

There are variations on the same theme (for example, using 'ne' instead of '!=' in the example above, using approximate comparison, and so on).


wdb The possibility to interpret the meaning of ?set in more than one way leads to the claim to use names which tell more precisely what they are intended for. For case #1, the appropriate name should be setExistingVar ..., and for case #2, the appropriate name should be setVarToVal ...


DKF: From Tcl 8.5 onwards, there's also a need for ?incr (which would be the old 8.4-and-before behavior).