Monday, April 29, 2013

the super loop

I have a confession to make. I really prefer the C syntax with braces over the keyword-bracketed do...od type of syntax that I chose to talk about Unobtainabol. The only reason that I went with the keyword-bracketed syntax was to support this notion of a super loop that has been tasking me for several years now. Even more embarrassing, the super loop is really more of a syntactic curiosity than a real language feature and I believe that language design really ought to have moved on from syntax by this point. However, the super loop is neat so I'm throwing out principle and adding it to Unobtainabol.

Here is the basic intuition behind the super loop. There are various versions of while and until loops in various languages, some of which test at the top and others of which test at the bottom. Of course, since you sometimes need to test in the middle, these languages also include a special break statement for breaking out of loops from the middle. In addition, many languages also have a special iterator loop to iterate through collections. My thought is, why not make the whole thing more elegant (one of the goals of Unobtainabol) by having a unified do-anything loop? So here is the Unobtainabol super loop:
super-loop: do  loop-statementod
The "+" following loop-statement should be read as "one or more of loop-statement". A super-loop will loop endlessly through the list of loop-statement+ unless a loop-statement causes an exit. Any declarations made in the body of the super-loop is active from the point of declaration to the end of the super-loop. A loop-statement is a regular statement or one of the following special statements called a loop-controller:
loop-statement: statement | loop-controller
loop-controller:
  loop |
  then |
  finally |
  while condition; |
  until condition; |
  for generator-assignment+; |
A loop-controller has a special significance to the execution of the super-loop as follows:
loop: any statement that appears before loop is executed only once before the first iteration begins. The statements before loop typically include declarations and initializations. No other loop-controller may appear before loop. Notice that loop is optional. It is only needed if you have some initialization.
then: any statement that appears after then and before another loop-controller is executed only once after the last iteration of the loop and only on a normal exit from the loop (that is, not a thrown exception). The statements after then typically include finalization and cleanup code. No loop-controller other than then or finally may appear after a then.
finally: any statement that appears after finally and before another loop-controller is executed only once after the last iteration of the loop on either a normal or abnormal exit. The statements after finally typically include finalization and cleanup code. No loop-controller other than then or finally may appear after a finally. If there are multiple instances of then and finally the statements following them are all executed in order on a normal exit, but on an abnormal exit, only the statements following a finally are executed.
while condition: execution of a while statement evaluates condition. If condition is false, then a normal exit from the loop occurs immediately.
until condition: execution of an until statement evaluates condition. If condition is true, then a normal exit from the loop occurs immediately.
Before describing the effect of the for statement, I have to explain what a generator-assignment is:
generator-assignment: var-declaration = generator | variable := generator
A generator is an expression that can return a sequence or set of results (more on that in a future post). Basically, a generator assignment is a declaration of a variable that is initialized with a generator or a variable declared previously that is assigned a generator expression. In the following description any such variable is called a generated variable. The for works like this:
for generator-assignment+: on the first iteration of the super-loop, the generated variables are set to the first generator value. At each subsequent iteration, the values are updated by the generators using a first-in-first-out cross-product evaluation of the generator-assignment expressions. When all generators are exhausted, a normal exit from the super-loop occurs.
Notice that the for can give you the effect of a nested loop because it acts like a nested loop over its generators, but no other loop-controller gives this effect. You can have multiple while, until, and for statements but they are all evaluated sequentially. To get the effect of a nested loop other than with multiple generator-assignment expressions in a single for, you have to nest the loops.
Here are some examples of the super-loop in action. I'll use ... to indicate a list of arbitrary statements. Here is how you loop forever (say as the main loop of a daemon process):
do
  ...
od
These three all do the same thing:
do
  var i:int=0
loop while i < 10;
  ...
  i := i+1;
od
do
  var i:int=0
loop until i >= 10;
  ...
  i := i+1;
od
do for var i := 0 to 9;
  ...
od
These two do the same thing again, but with the test at the end
do
  var i:int=0;
loop
  ...
while i < 10;
  i := i+1;
od
do
  var i:int=0;
loop
  ...
until i >= 10;
  i := i+1;
od
This one does something similar to the test-at-end loops, but i is not visible in the body of the loop
do
  ...
for var i := 0 to 9;
od
This loop works like a nested loop, iterating for every combination of i, j, and k having the values from 1 to 10
do
  var i,j,k
loop for i := 1 to 10, j := 1 to 10, k := 1 to 10
 ...
od
Don't pick on me for not showing the commas between the generator-assignments in the syntax. I didn't want to complicate it. Notice that you need the loop statement even though the only thing that comes before it is a declaration with no initialization. Otherwise the variables would be declared anew each time through the loop and the generators would loose the variables they are generating into.

The real power of super loops comes with combinations of loop controllers:
do
  var x :=
<something that has to be cleaned up>;
loop for const i := 1 to 10;
  const y := f(i,x);
while y < 10;
  const z := g(i,y,x);
until z > 10;
finally
<clean up x>
od
By the way, Unobtainabol still has a break statement. This is needed since while and until are part of the super-loop syntax and can only occur at the top level. Sometimes you need to break out of a loop from inside a nested compound statement.

No comments:

Post a Comment