Version 0 of Success-fail control structures

Updated 2004-01-02 18:05:19

Lars H: I had a look at Icon, and got interested in the unusual approach towards conditionals in that language. Some of that can (of course) be done in Tcl too, so I had a go at it. I find them intereting because they demonstate one way in which one could remove the expr "little language" from core Tcl (not very useful, but interesting for stylistic reasons).

The basic idea is that commands either fail or are successful. When some part of a composite command fails, the whole thing fails, and the other parts of it won't be executed. This is simply the way that Tcl return codes work, so we have that already. Failure could be coded as a straightforward error, but that carries some overhead in constructing the errorInfo (or at least some people are very keen on saying it does), so I will use a special fail command instead.

 proc fail {} {return -code 5}

The relevance this has for control structures (if, while, etc.) is that these use the success/fail status of the condition, rather than its value, to decide what to do. Therefore the operations can return something other than the boolean result as value. One example might be

 proc lcontains {list item} {
    set n [lsearch -exact $list $item]
    if {$n>=0} then {return $n} else {fail}
 }

which could be used as

 f-if {set n [lcontains {red green yellow blue} $col]} {
    # Success case:
    puts "This is colour $n."
 } {
    # Failure case:
    puts "I don't know that colour."
 }

(Whether this is good style is debatable, but in some cases that order of operations appears natural.)

A simple implementation is

 proc f-if {condition then else} {
    if {![catch {uplevel 1 $condition}]} then {
       uplevel 1 $then
    } else {
       uplevel 1 $else
    }
 }

and analogously

 proc f-while {condition body} {
    while {![catch {uplevel 1 $condition}]} {
       uplevel 1 $body
    }
 }

Defining > as another fail-type test,

 proc > {a b} {
    if {$a > $b} then {
       return $b
    } else {
       fail
    }
 }

one can write things like

 set n 1; set m -3
 f-while {> [> 14 $n] $m} {
    incr n 1; incr m 2
 }

although that has probably lost much of the beauty in the infix equivalent 14 > $n > $m (14 is greater than n AND n is greater than m).

In the construction of elementary operations department, one can note that logical and is simply ; -- a condition can be more than one command, and it only succeeeds if they all succeed. Logical not can be expressed in terms of f-if:

 proc f-not {condition} {f-if {uplevel $condition} {fail} {}}

and then logical or is simply a matter of suitable double negation:

 proc f-or {args} {
    f-not {
        foreach condition $args {
            f-not {uplevel $condition}
        }
    }
 }

[ Category Control Structure | Category Concept ] ?