Is there a proper way to "break" out of a switch script and remain in the procedure other than wrapping in a loop?

92 views Asked by At

I was trying to not repeat the same code in different switch blocks and could not figure out how to break out a switch script except to return and exit the procedure.

My question is, Is wrapping the switch in a while loop simply to break, when desired, a terrible thing to do and is there a better way?

It took me awhile, but I realize that a break is not necessary because could set variable in the catch and test outside it to determine when the rest of the code under opt_2 is run (as in the second code example below); but having a break is more clear, especially when the real code is a bit more involved than this example.

Thank you.

% proc SwitchBreak {condition valid} {
  set ui_state 1
  while { 1 } {
    switch -- $condition {
       opt_1 {
         chan puts stdout opt_1
       } opt_2 {
         chan puts stdout opt_2
         if { [info exists ui_state] && $ui_state == $valid } {
           if { [catch { throw {Database Error} {Failed to Write} } result]} {
             chan puts stdout "${result}. Rolled back"
             set rv 1
             set response {Operation Failed}
             break
           }
         }
         chan puts stdout {Opt_1 code to run only when\
            no valid ui_state or valid state succeeds.}
         set rv 0
         set response {Successful write.}
       } opt_3 {
         chan puts stdout opt_3
       } default {
         chan puts stdout opt_4
       }
    }
    break
  }
  chan puts stdout {Code after the switch.}
  chan puts stdout "rv: $rv; response: $response"
}
% SwitchBreak opt_2 1
opt_2
Failed to Write. Rolled back
Code after the switch.
rv: 1; response: Operation Failed
% SwitchBreak opt_2 0 
opt_2
Opt_1 code to run only when no valid ui_state or valid state succeeds.
Code after the switch.
rv: 0; response: Successful write.

No loop, no break.

proc SwitchBreak {condition valid} {
  set ui_state 1
    switch -- $condition {
       opt_1 {
         chan puts stdout opt_1
       } opt_2 {
         chan puts stdout opt_2
         set rv 0
         if { [info exists ui_state] && $ui_state == $valid } {
           if { [catch { throw {Database Error} {Failed to Write} } result]} {
             chan puts stdout "${result}. Rolled back"
             set rv 1
             set response {Operation Failed}
           }
         }
         if { $rv == 0 } {
           chan puts stdout {Opt_1 code to run only when\
              no valid ui_state or valid state succeeds.}    
           set response {Successful write.}
         }
       } opt_3 {
         chan puts stdout opt_3
       } default {
         chan puts stdout opt_4
       }
    } 
  chan puts stdout {Code after the switch.}
  chan puts stdout "rv: $rv; response: $response"
}

% % 
% Switch opt_2 1
opt_2
Failed to Write. Rolled back
Code after the switch.
rv: 1; response: Operation Failed
% SwitchBreak opt_2 0
opt_2
Opt_1 code to run only when no valid ui_state or valid state succeeds.
Code after the switch.
rv: 0; response: Successful write.
1

There are 1 answers

3
Donal Fellows On BEST ANSWER

While this is legal in that you can write it and it will execute correctly, it seems very ugly to me; a simple if...else would appear to be able to express the intent while actually being clearer. I would even go as far as to say that I would expect that to normally be true.

# Just the bit inside the switch case
chan puts stdout opt_2
if {
    [info exists ui_state] && $ui_state == $valid
    && [catch {
        # This is a stand-in for something real, right? Right?
        throw {Database Error} "Failed to Write"
    } result]
} then {
    chan puts stdout "${result}. Rolled back"
    set rv 1
    set response "Operation Failed"
} else {
    chan puts stdout "Opt_1 code to run only when\
        no valid ui_state or valid state succeeds."
    set rv 0
    set response "Successful write."
}

Would there be a case where doing this convoluted thing with a run-once while is best? I doubt it... but never say never.


A run-once for would be slightly better, provided you put break in the increment clause. Like that, it's clearer at the top what you are doing.

Or you could use try with an on break clause.