Tutorial: Control Structures


  Now that all the interesting stuff has been dealt with, we have to cover a few odds and ends. This section is about the imperative aspect of gbeta, namely the built-in imperatives for unusual but structured transfer of control. Ah, by the way, leave and restart are not boring, even though they are imperative constructs.

Standard control structure imperatives

Since the 70'ties, all imperative languages (including the object-oriented subset) have had built-in support for "structured" versions of GOTO, now that GOTO had been deemed harmful..

As a matter of fact, BETA was designed to have a minimal collection of such constructs, since the generality of the pattern concept allows it to support all kinds of customized control structures. As an example, look at scan in the container pattern of example 6 in the section about virtual patterns, and the cycle pattern of example 8.

The if-imperative comes in a compact version:

"(if" <Evaluation> "then" .. "else" .. "if)"

and in a version which is similar in use to "switch" or "case" statements in other languages:

"(if" <Evaluation>
"//" <Evaluation> "then" ..
"//" <Evaluation> "then" ..
..
"else" ..
"if)"

The first <Evaluation> is computed and the value compared for equality with the <Evaluation>s after the "//" one by one, and the block of imperatives ("..") after the first matching value is executed. If no equal values are found, the else imperatives are executed.

Finally, iterating a pre-computed number of times is supported with the for-imperative which also comes in two variants:

"(for" <Evaluation> "repeat" .. "for)"

and

"(for" <Name> ":" <Evaluation> "repeat" .. "for)"

The first variant just repeats the body ".." <Evaluation> times, and the second does the same, with <Name> denoting the number of the current iteration, i.e. 1 the first time round, 2 the second etc. <Name> is not an object, it just denotes the value, so it cannot be changed by assignment, and the type of it cannot be computed (using "##"), and it is not allowed to ask for a reference to it (using "[]").

Jumping around, and unwinding the stack

The two last constructs for the transfer of control are very expressive, even if they look rather ordinary at the first sight. They are called leave and restart, and they are used in connection with some kind of block, e.g. a main part.

The first one:

"leave" <Name>

jumps to the end of the block denoted by the <Name>, i.e. it forces the execution of the block to terminate earlier than it otherwise would. The other one:

"restart" <Name>

jumps to the beginning of the block denoted by the <Name>, i.e. it forces the execution of the block to start from the beginning again.

The reason why this is not entirely trivial is that we may have (due to the coercion) objects on the stack between the one associated with the enclosing block and the one which is currently executing. In other words, leave may imply a stack unwinding which forcefully terminates the execution of any number of objects before the target object (associated with the target position in the source code) is at the top of the stack. Moreover, the number of objects to unwind off the stack may not be statically determinable.

This resembles the exception handling mechanisms of other languages, e.g. the try and catch pair of C++. The most important difference is that the combination of coercion, block structure, and leave/restart makes it possible to have a statically determined target for the "long jump" in BETA, whereas the stack unwinding in e.g. C++ only terminates in an ordered fashion if a catcher happens to be on the run-time stack. In Java, this (missing) safety property has been reinstated by using strict exception declarations on all methods. On the other hand, this pervades the entire program, even if lots of intermediate routines would not have to depend on the given exception handling strategy.

Example 10

Here is an example which by the way also demonstrates how it is possible to create a recursive procedure which will call the specialized version of itself in specializations. This is not a problem with gbeta, it's an obstacle which never occurs in languages that do not support specialization of behavior in the first place, such as anything but BETA and CLOS.. Anyway, we need a notion of selfType or myType or whatever else that concept has been designated in the literature, and again coercion comes to the rescue, since this(recur) denotes the currently enclosing object which is coerced into a pattern because it is used in a pattern context. As a result, selfType is declared to denote the pattern of the enclosing object, and the type system recognizes that this type is more specialized in the context of the anonymous specialization of recur which occurs in the main do-part of the program.

(* FILE ex10.gb *)
-- betaenv:descriptor --
(# 
   recur:
     (# selfType: this(recur);
        enough:< object;
        depth: @integer
     enter depth
     do (if depth>4 then enough if);
        depth+1->&selfType
     #)
do 
   L: recur(# enough::(# do leave L #)#)
#)

You might want to compare this with the following slight variation:

-- betaenv:descriptor --
(# 
   recur:
     (# enough:< object;
        depth: @integer
     enter depth
     do (if depth>4 then enough if);
        depth+1->&recur
     #)
do 
   L: recur(# enough::(# do leave L #)#)
#)

The "&" operator which marks explicit object instantiation is covered later. Just before that, the next topic is repetitions.

 


Signed by: eernst@cs.auc.dk. Last Modified: 3-Jul-01