Peter Lewerin (2013-12-26): I can't believe this basic trick hasn't been described here yet, but I can't find it even after repeated searches. I'll put it up here, and if it's already on the wiki somewhere, let me know and I'll nominate this page for deletion.
A common idiom in programming is "use this value unless it isn't what I want to have; in that case use this default value". For instance, when assigning a value to a variable, it can be useful to make sure that the variable gets some safe value instead if the intended value isn't up to snuff.
For me, it's often "I'd like to use $a unless it's an empty string, in which case I'll use $b". So I have this little proc which I tend to copy into my projects (as you can see, it's possible to have some value other than an empty string as the undesired value; actually I have never used that option except when testing the command):
proc either {value default {nullval {}}} { expr {$value ne $nullval ? $value : $default} } # use like this: set foo {} either $foo Tcl # -> Tcl set foo xxx either $foo yyy # -> xxx either $foo yyy xxx # -> yyy
A variant of this is to define the unwanted value as any logically false value (0, false, or no in Tcl): this works like the or operator (however it is written) in some languages (like Scheme's or or Perl's ||) but not as in other languages which either 1) don't accept non-boolean values as operands to or (Tcl doesn't), or 2) always has the or operator return a boolean value, or both.
proc eitherOr {value default} { expr {$value ni {0 "false" "no"} ? $value : $default} } set foo xxx eitherOr $foo yyy # -> xxx set foo no eitherOr $foo yyy # -> yyy
Another variant is to instead of an unwanted value use however the language defines the undefined state. Some languages have operators (known as null coalescing operators) for this, such as C#'s ?? or Perl's //; Scheme's or works here too. Tcl doesn't have a value for undefined / null, but at least we can test a variable for existence:
proc eitherExists {varname default} { upvar $varname _varname expr {[info exists _varname] ? $_varname : $default} } set foo xxx eitherExists foo yyy # -> xxx unset foo eitherExists foo yyy # -> yyy
Finally (?) we can use a provided predicate function to tell us whether to use the value or the default value. Note that the function must be an actual predicate, i.e. return a legal boolean value (1, true, yes, 0, false, or no).
proc eitherApply {value default func} { expr {[apply $func $value] ? $value : $default} } set foo xxx eitherApply $foo yyy {{val} {string match x* $val}} # -> xxx set foo aaa eitherApply $foo yyy {{val} {string match x* $val}} # -> yyy
This is of course the most generic variant. Actually it can be used to redefine the original command:
proc either {value default {func {{val} {expr {$val ne {}}}}}} { expr {[apply $func $value] ? $value : $default} } set foo {} either $foo Tcl # -> Tcl set foo xxx either $foo yyy # -> xxx either $foo yyy {{val} {expr {$val ne "xxx"}}} # -> yyy
aspect: thanks for refreshing this - I love the name [either] and I'm definitely stealing that! Not so sure about the subcommands .. they might be a good case for sprucing up with options and arguments. A similar pattern I've been using a lot lately but irritatingly can't find a good name for is:
proc ifnotempty {_var args} { upvar 1 $_var var if {[info exists var] && $var ne ""} { uplevel 1 $args } }
I've considered [maybe], [iff] (which might be more like an error-suppressing if) .. but nothing quite fits.
AMG: As I wrote on the [append] page, [append] can be used to set a variable to empty string if it doesn't already exist, leaving its value alone otherwise. This is often all that's needed. Just do:
append var ""
As a bonus, this returns the variable's value (or empty string if it didn't exist), so just write [append var ""] instead of $var or [set var].