TIP: 70 Title: A Relational Switch Control Structure Version: $Revision: 1.8 $ Author: Bhushit Joshipura Author: Donal K. Fellows State: Withdrawn Type: Project Vote: Pending Created: 20-Oct-2001 Post-History: Tcl-Version: 8.5 ~ Abstract This TIP proposes the introduction of a new control structure, ''rswitch'', which is a relational parallel to switch-case control structure. It consists of two lists: condition list and situation-reaction list. At the maximum two conditions can be specified. Based on situation, reaction is executed. The situation is selected on "first true and only the first true" basis. ~Rationale Theoretically only two controls - ''if'' and ''goto'' - are sufficient to implement all algorithms. However, languages provide more control structures for better representation of algorithms. To many structural programmers like me, a ''switch'' statement gives much better picture of the program than equivalent ''if''-''elseif''-...-''else'' chain. It pronounces the course of decision of a big chunk of program in a single statement, making understanding and maintaining software easier. It also helps to optimize the software better if it is written in ''switch'' form. However, ''switch'' is strictly data based i.e. ''switch'' happens strictly on data value. The proposed ''rswitch'' command is a control structure similar to (and more general than) ''switch''. (As a matter of fact, Tcl's ''foreach'' control structure is a special case of its for control structure.) Using ''rswitch'' it should be possible to take decisions based on relations between entities. In response to comments on Revision: 1.2 of the draft, Bhushit Joshipura wrote [Re-edited]: Why rswitch? [Re-written] 1. if..elseif..else contains elements of surprize spread in the code. From maintenance point of view, once the ''if''..''elseif''..''else'' code goes beyond horizon (one display length), burden of reference retention comes on human brain making maintenance bug-prone. 2. In case of ''rswitch'', if a situation refers to one or more conditions, we had reduced this burden by stating upfront which variable is in the spotlight. The maintainer can then easily jump irrelavant cases. 3. In ''if''..''elseif''..''else'', a string of three conditions (with one of them being a `not' string) can mystify me for at least an hour. 4. In case of an ''rswitch'' (even in a situation which does not refer to any conditions): > 1. `and' is nested rswitch > 2. `or' is a fall through case > 3. `not' could be written as "default" case. `not' free from `and' and `or' is less confusing. We can write almost a `not-less' code using "default". > Logical connectives result into visual presentation. 5. Object Oriented Programming is trying to eliminate ''switch-case'' statements by identifying localization of references and finding a heirarchy of data-actions. 6. In similar way one can think of ''rswitch'' cases (situations). Identifying localized references (w.r.t. situations) and a heirarchy of situations-reactions. However, this is a research issue. Moreover, the writer does not need to be an artist to be able to write understandable code. ~Implementation in Other Languages I queried about proposal of such a control structure in C to Dr. Dennis M. Ritchie in February 2001. (At that time I thought only of bi-conditional relational switch. See a few pages down for currently proposed control structure.) > Absence of relational switch I know this can be odd for other languages - but not for C. C is so near to machine and a relational switch could be ideal for many many machine-cycle saving situations. > Apart from machine-orientedness, it could avoid many usages of not-so-structured ?: operator. > It could simplify a lot of control and signal processing code too. > Why did C become more data-biased for a control structure? | relational-switch(expr1,expr2){ | case ==: statements; | break; | case > : statements; | break; | case < : statements; | break; | default: statements; | break; | } TCL need not be so optimized, as C has to be. However, clarity and maintainability remain formidable reasons for relational switch implementation. In a quick reply, Dr. Ritchie wrote back: > The relational switch idea is (so far as I know) for C a new suggestion, although I have no idea of all things that were proposed for the 1989 or 1999 C standards. If seems to hark back to the old Fortran GOTO statement | IF (expression) 2, 3, 4 > which went to various places depending on whether the expression was -, 0 or +. It's also a bit strange syntactically (though it might work in the grammar) in that the case values aren't expressions, but just operators. > Regards, Dennis" Thus the structure is absent from C and its whole family. It is absent from Pascal, PERL, BASIC, shell scripts - and of course, TCL. Fortran's computed goto is near to bi-conditional rswitch. (That way Chimpanzees are near to Homo sapiens too.) However, clarity of presentation of default and fall through are not achievable through computed goto. Mono-conditional rswitch, however, does not have a parallel in languages of my knowledge (C, C++, Java, Pascal, BASIC, Fortran, shell scripts). ~Grammar and Behavior Overall: |rswitch {[condition(s)]} { | { | | } | ... |} The condition list may have no, one or two variables. A situation is legal if: 1. It is a valid expression or 2. If the condition list had at least one element 'x', {$x $situation} is a valid expression or > 1. If the condition list had exactly one element 'x', {$situation $x} is a valid expression or > 2. If the condition list had two elements 'x' and 'y', {$x $situation $y} is a valid expression or 3. If the condition list had two elements 'x' and 'y' and {$situation $y} is a valid expression or 4. If $situation == "default" A reaction block is legal if: 1. It is not the last block and $reactionBlock == "-" (fall through) or 2. It is a valid TCL action block Let us call a non-default extracted valid expression a SITUATION. At execution, reaction block following or fell through by the first and only the first SITUATION that becomes true, is executed. In case no SITUATION becomes true and default situation is present, reaction block following or fell through by default statement is executed. Default situation is not necessary for operation of rswitch. An rswitch without any situation-reaction block is grammatically valid. ~Sample Invocations |# Full length condition block. Second condition perhaps got redundant with maintenance. |rswitch {$a $b} { | {> $c} - | {< $d} { | puts "$a is either > $c or < $d or both" | } | {< $c} { | # Full length condition block. Second condition is used. | rswitch {$a $d} { | > { | puts "$a is < $c AND > $d" | } | == { | puts "$a and $d are equal and they are < $c" | } | default { | puts "$a is < $c BUT <= $d" | puts "should never come here" | } | } | } | {3 > } { | puts "$a == $c, $a >= $d and $b <= 3" | } | default { | puts "$a == $c, $a >= $d and $b >= 3" | } |} ~Contrast Contrast above code with its if-elseif-else equivalent. Notice that: 1. Both the examples have same effect. 2. Both examples are indented with the same style. |if {($a > $c) || ($a < $d)} { | # could you see a maintenance nightmare that could have arisen when | # reference to $b were eliminated? | puts "$a is either > $c or < $d or both" |} elseif {$a < $c} { | if {$a > $d} { | puts "$a is < $c AND > $d" | } elseif {$a == $d} { | puts "$a and $d are equal and they are < $c" | } else { | puts "Pop-up question: What should we have here? | puts "$a is < $c BUT <= $d" | puts "should never come here" | } |} elseif {3 > $b} { | puts "$a == $c, $a >= $d and $b <= 3" |} else { | puts "$a == $c, $a >= $d and $b >= 3" |} ~Responses to Revision 1.2 Revision 1.3 tries to reflect suggestions from various of the following contributors. Thanks. John Ousterhaut wrote: > This is certainly a novel suggestion, but I'm not sure how useful it is. The proposed new command doesn't seem much clearer or much more efficient than an "if... elseif ... elseif..." statement. One of the arguments for a "switch" statement over "if ... elseif ..." is that there can be many branches in a "switch" statement. However, it seems unlikely to me that there would be more than a couple of branches in the proposed new "rswitch" statement, so its value seems marginal to me. In the absence of compelling value, I'd suggest leaving it out to avoid language bloat. Kevin B. Kenny wrote: > I don't want to discourage language experimentation, but I question whether the Tcl core is the right place to do it. The 'rswitch' that is being requested can be done just as well with an extension - except for bytecode compilation, which can come later -- and exposed to programmers that way. If it becomes sufficiently popular, it can then be integrated into the core. Don Porter wrote: > Agreed. A natural place to offer this command would be in the control package of tcllib. Let's put it there, and if it proves to be indispensible, we can consider moving it into Tcl itself at that time. Mohan L. Gundu wrote: > What I had in mind is to extend the 'rswitch' case block so that it can accept multiple conditional relationships in a single statement. > I can also think of another usage of 'rswitch' which doesn't take any arguments at all. A vanilla version which is just replacement to if-elseif-elseif-..-else structure but only that code is more easier to read... |rswitch { | ($a > 4): /* block 1 */ | ($b < 100): /* block 2 */ | ($c > 5 ): /* block 3 */ | ($d == 10): /*block 4 */ |} Don Porter wrote: > Rather than defining two forms of the command, mono-conditional and bi-conditional, why not use the power of Tcl to allow for both and even more possibilities within a singleform? > Consider: |rswitch $formatString { | $sub1 $body1 | ... | $subN $bodyN |} > Then have [rswitch] construct the Tcl expressions to be tested using [format]: |format $formatString $sub1 > So you could have: |rswitch {$a %s $b} { | > {puts "$a is greater than $b"} | < {puts "$a is less than $b"} | == {puts "$a equals $b"} |} > or |rswitch {$a %s} { | 1 {puts "$a > 1"} | 5 {puts "$a > 5"} | 15 {puts "$a > 15"} | {>$b} {puts "$a > $b"} | {<$b} - > {==$b} {puts "$a <= $b"} |} > Extending this idea further, consider the possibility of using [[format]] to create the expression like so: |eval [linsert $sub1 0 format $formatString] > Then the substitutions could be lists of multiple values to substitute into multiple %-conversion specifiers in the format string, allowing for the construction of quite elaborate expressions. Brent Welch wrote > I like Don's suggestion. I'm reminded of the switch statement in the tclhttpd state machine, crafted by Steve Uhler: |set state [string compare $readCount 0],$data(state) |switch -glob -- $state { | 1,start { # Read something in the start state } | 0,start { # Read empty line in the start state | 1,mime { # Read something in the mime state } | 0,mime { # Read blank line in the mime state } | -1,* { # Read error in any state } | default { # Unexpected condition } |} > I had had a bunch of nested if-then-else's, of course. With an artful creation of the switch value and the power of things like glob, you can really create compact, expressive switch statements already. ~Sample Implementation Will be provided later. ~Copyright This document is placed in public domain.