''[Peter Lewerin] ([Peter Lewerin%|%content disclaimer%|%]) (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. [PL]: the term ''iff'' is usually understood as meaning "if and only if", so I wouldn't use that. Maybe `ifhas`? [aspect]: Oops - I mis-copied the code. With the `{*}` hopefully it makes more sense, and makes the comment I deleted redundant. ([PL]: I'm putting it back inside a discussion tag for the time being, we'll delete it later when the discussion is over.) No, the `{*}` doesn't necessarily make very much more sense. The `[uplevel]` in effect already applies an implied `{*}` to `$args` (just like `[eval]` does), so what you get is a double expansion of the argument list. This means that the user can now either provide a script as a sequence of arguments: === ifnotempty foo puts Foo ; puts Bar === or as a script body: === ifnotempty foo { puts Foo ; puts Bar } ;# could've been on separate lines === or both (the semicolon seems out of place, but is necessary here): === ifnotempty foo { puts Foo ; } puts Bar === While this kind of freedom might seem like a nice thing (I suspect the [Perl] crowd would be all over it `;)`), ultimately it will, I think, be confusing to the user. My personal preference is to make the code unambiguous in what it accepts. The `uplevel 1 $args` form accepts a sequence of arguments but not a script body, the `uplevel 1 $body` form accepts the latter but not the former (well, it ''does'' accept single-word command invocations with or without braces, such as `[pwd]`), the `uplevel 1 {*}$args` form accepts both and a mixture of both. I would go with the second or first form, but not with the third. [aspect] - well said. I will point out that the behaviour with {*}$args more closely mimics Tcl core commands, including [uplevel]. I've always found this "implicit concat" behaviour a bit unnerving (see also: [after], [eval], ...). I can see how it served to make command/script composition less ugly in the absence of {*}, but I always find myself second-guessing where to wrap arguments with [list] -- and sometimes I get it horribly wrong, as in this example :-). Hm, I just searched for [implicit concat] and found no obvious hits. This discussion almost warrants a page of its own .. [PL]: actually, I think `after`, `eval`, and `uplevel` are the only ones that do concat-and-evaluate. `[catch]` doesn't, for instance (the command syntax would need a major rewrite to make that possible). There are lots and lots of control commands that take singular script arguments, however. I also thought of another point earlier but forgot to mention it. If we rename the different versions `ine1` (your original version, with `uplevel 1 $args`), `ine2` (my version, with `uplevel 1 $script`), and `ine3` (your later version, with `uplevel 1 {*}$args`), and set the command to print a) the value of `foo`, and b) the value of `msg` (which is the text "foo bar"): ====== % set foo bar # -> bar % set msg "foo bar" # -> foo bar % ine1 foo puts $foo # => foo % ine1 foo puts $msg # => foo bar % ine2 foo puts $foo # =2> wrong # args: should be "ine2 _var body" % ine2 foo { puts $foo } # => foo % ine2 foo { puts $msg } # => foo bar % ine3 foo puts $foo # => foo % ine3 foo puts $msg # =2> can not find channel named "foo" ====== I'd say that it's not quite obvious what's wrong with the last invocation (and why the next-to-last invocation is ok) unless you are familiar with the implementation of the command (the problem with the first invocation of `ine2`, in contrast, is fairly obvious even from the automatically generated error message). Again, the `ine1` and `ine2` commands are internally consistent, while the `ine3` command seems inconsistent (of course, it isn't really, but seeming inconsistency is almost as bad as real inconsistency). <> (PL's earlier suggestion) Also, you might possibly want to consider having the body to be executed as a single argument instead of collecting the rest of the arguments, like this: ====== proc ifnotempty {_var body} { upvar 1 $_var var if {[info exists var] && $var ne ""} { uplevel 1 $body } } % ifnotempty foo { puts Foo puts Bar } ====== This has three advantages: 1) it separates the variable from the code, 2) it looks more like other similar Tcl control structures, and 3) it lets the user provide several commands without having to cram them into one line with semicolons in between. Just a thought. It's not in any way a necessary change: the command certainly works as you defined it. <> ---- [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]]`. [PL]: that would indeed equivalent to using `eitherExists` above with the invocation `eitherExists var ""`. It doesn't handle any of the other cases, though. <> Category Concept | Category Example | Category Functional Programming | Quick'n'dirty