Version 12 of Why Tcl has no GOTO command

Updated 2014-05-25 21:59:13 by dkf

Q. Why doesn't TCL have a 'goto' command?

A. (Donal Fellows) Because "goto"s are the bane of structured programming. Let's examine what you might use them for:

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;
   }
  }
 :found

ulis,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. [L1 ] :

"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!

DKF: It turns out you can have goto in Tcl after all. Yes, real goto. All it takes is writing some bytecode…

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


See also