Success-fail control structures

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).

(escargo The infix form is the way Icon does it, of course.)

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}
        }
    }
}

escargo 2004-01-02: As an old Icon programmer, I feel I need to note that in Icon, expressions either fail or they succeed and return a value. An expression like 2 > 11 fails; 2 < 11 succeeds and returns a value of 11. That's why 2 < 11 < 22 works. Since < is associative, the expression 2 < 11 is evaluated first, returning a value of 11, and then 11 < 22 is evaluated. (There are many other aspects of Icon in returning a value that this does not touch on, such as generators and resumption of expressions. It is a language whose syntax belies its semantic variety.)