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