1.36. The ubiquitous goto

Felix allows statements to be labelled, and provides a conditional and unconditional goto statement.
Start C++ section to tut/examples/tut144.flx[1 /1 ]
     1: include "std";
     2: print "start"; endl;
     3: var i = 1;
     4: start_loop:>
     5:   if (not (i<10)) goto end_loop;
     6:   print i; endl;
     7:   i++;
     8:   goto start_loop;
     9: end_loop:>
    10:   print "finished"; endl;
    11: 
End C++ section to tut/examples/tut144.flx[1]
Note that labels require a terminating :> rather that C's plain :.

Goto is not recommended programming style, but it is an essential primitive, and it is used both by the compiler and in the standard library to define control structures such as 'while'.

It is also useful for inventing your own control structures: together with higher order functions and currying, you can do this quite easily in Felix, as we shall soon see.

But first consider the following example:

Start C++ section to tut/examples/tut145.flx[1 /1 ]
     1: include "std";
     2: 
     3: var x = 1;
     4: proc run()
     5: {
     6:   proc check()
     7:   {
     8:     if(x!=1) goto bad_x;
     9:   }
    10:   check();
    11:   print "good x"; endl;
    12:   x = 2;
    13:   check();
    14:   print "good x"; endl;
    15:   goto finished;
    16: 
    17: bad_x:>
    18:   print "bad_x"; endl;
    19: 
    20: finished:>
    21:   print "finished"; endl;
    22: }
    23: run();
    24: 
End C++ section to tut/examples/tut145.flx[1]
In this example, you see what is called a non-local goto: a jump into an enclosing procedure. Such jumps are quite OK.

What you can't do is jump out of a function, that is, across a function boundary. [the reason will become clear much later]

There is another interesting fact. A procedure nested in a module, including the top level, cannot jump to a label in the module. The reason is that executable code in a module, including any labels, are wrapped in an initialisation procedure, which is called where the module is defined. That initialisation procedure does not have the procedures defined in the module nested in it, instead, they're at the same level as the initialisation procedure. You can't jump into the initialisation procedure because it mightn't be active at the time the other procedure is called.

[This may change: a better model would be a that a module returns the enities exported from it as a tuple: the procedures would then be nested in the initialisation routine. This would also make modules first class objects, with a type described more or less as a tuple. In turn this allows dynamic binding.]