1) Implementing a standard control structure.
This simply doesn't fit with the Tcl way of doing things. Remember, if, for and while are all ordinary commands with no special privileges. If you want, you can even create your own control structures which are just as much a part of the language as they are.proc repeat {script untilKeyword expression} { if {![string equal $untilKeyword "until"]} { return -code error "malformed repeat: should be \"repeat\ script until expression\"" } uplevel 1 $script set test [list expr $expression] while {![uplevel 1 $test]} { uplevel 1 $script } }
2) Resource cleanup on error exit.
Most Tcl resources are cleaned up automatically on exit from scope. The rest can be handled with the use of catch or something built on top of it; it is a better way to do it too, as it is writing code that is saying what you actually mean. (Alas, the code for try/finally is a bit too long for me to reproduce here.)KBK (8 November 2000) -- Here's one possible implementation of try ... finally ...KPV another C idiom for doing this to use 'do { ... } while(0);'. Inside that loop you have breaks, which essentially are goto's to the end of the loop.Lars H: Examples of how that idiom can be coded in Tcl:foreach dummy 1 { # Code to jump out of goes here. } for {} 1 break { # Code to jump out of goes here. } while 1 { # Code to jump out of goes here. break ; # Important! Needed to terminate loop. }
3) Creating a state-machine.
It is equally possible to use a mechanism based on Tcl arrays and eval to do this, like this:array set transition { initialState state1 state1 { set state state2 set x 1 } state2 { set state state3 incr x $x } state3 { set state [expr {($x < 10) ? "state2" : "state4"}] } state4 { break } } set state $transition(initialState) while {1} {eval $transition($state)} puts $x ;# Can you guess what this does without running the code?This can even be extended fairly easily into working over events. I leave that as an exercise though.4) Err. I can't think of a 4) at the moment. :^)As you can see, you can do a lot with Tcl even without a goto. It often even ends up clearer to the person maintaining the code like that. Everyone's a winner!
David Cuthbert comments on 4):If you're a code generator, you'll often use a goto for a combination of the above reasons. Of course, if you're a code generator, you're not human, and your code is not intended to be read by humans.You're also probably spitting out C or assembly instead of Tcl. :-)
KPV more comments on 4)In C, I've had to use a goto to break out of multiple loops. Newer languages have labeled loops and you can break to a label. For example:
for (i = 0; i < 100; ++i) { for (j = 0; j < 100; ++j) { if (IsFound(i,j)) goto found; } } :foundulis,2002-09-03: You can easily have break labels in Tcl.
SS Also note that you don't need break to exit from switch branches (like in C), so one of the uses of goto in C (to exit a while loop that contains switch) is a non-issue in Tcl:
while(1) { x = get_next_x_value(); switch(x) { case 1: ....; break case 2: ....; break case 3; goto out; } } out: something();In Tcl a break inside the switch will exit the while loop in similar code. Also note that in C is natural to have goto, because C maps quite directly to machine language, that's not the case of a scripting language like Tcl, this is why I use goto in C quite often, and I'm sure the way I use it makes the code more clear instead of spaghetti, but at the same time I don't want goto in Tcl because I feel it useless and probably dangerous for the code quality.History corner: on 1993-05-21, JO posted in c.l.t. [1] :
- "Actually I don't have a strong philosophical objection to goto. I think that goto's should be used very sparingly, but there are definitely situations where programs are a lot clumsier without them. The reason they're not in Tcl is that I haven't been able to figure out how to implement them (the structure of Tcl and its interpreter make it hard to skip backwards or forwards)."
Hackery! edit
DKF: It turns out you can have goto in Tcl after all. Yes, real goto. All it takes is writing some bytecode with the assemble command…proc foo a { tcl::unsupported::assemble { expr { $a eq 20 } jumpTrue check expr { $a eq "abc" } jumpTrue check2 jump end label check eval { puts "Given value is integer" } pop jump end label check2 eval { puts "Given value is alpha" } pop label end # There *must* be one result value pushed onto the stack at the end push "" } }See those jumpTrue and jump instructions? They are your conditional and unconditional gotos.However, it is important to remember to pop results you don't need and have one value on the stack at the end. In fact, here's how (a simple) if and while might be done (without the exception handling):if
if {$x > $y} {puts "gt"}
tcl::unsupported::assemble { expr {$x > $y} jumpTrue body jump emptyElse label body eval {puts "gt"} jump end label emptyElse push "" jump end label end nop }while
while {$x > $y} {puts [incr x -3]}
tcl::unsupported::assemble { label start expr {$x > $y} jumpTrue body jump end label body eval {puts [incr x -3]} pop jump start label end push "" }Much more complicated patterns are possible. However, if you find yourself needing a complicated pattern of pops, you're probably doing something horrible.
bll 2017-2-5 c.f. http://koblents.com/Ches/Links/Month-Mar-2013/20-Using-Goto-in-Linux-Kernel-Code/DKF: goto is a sharp tool, and a fundamental programming primitive that is usually better avoided if other tools (if, while, try, etc.) are available. The fewer higher-level structure commands you have, the more you need goto. Now, C is pretty low level (hence the discussion you refer to) and lacks some things like try that would greatly reduce the amount of messing around, but Tcl is much higher-level; we can make our own program structure commands in Tcl (thanks to uplevel) and that hugely cuts true demand for goto. Also, Tcl's implementation does reference counted values, which reduces the amount of manual work needed by script authors to clean up (the big use for goto in Linux).I guess there might be situations where a goto is the clearest approach. I've not encountered one yet in my own code, but if there is one, the above trick with assemble is the best approach I know of, especially as it can use expressions and scripts pretty directly.