UNTIL Considered Harmful
A Case Against the UNTIL Statement would have been accurate, but I could not pass on using a famous phrasal template.
Conditional Iteration with UNTIL
An ABAP language element implements a concept needed to solve a problem in the developer mind. After a learning period, the developer will absorb the pattern and intuitively, effortlessly express a solution in term of the ABAP statement. The statement becomes part of the formal language used to describe a solution that other developers can read and understand.
The VALUE #( FOR .. THEN ..UNTIL ) expression works well in most cases, it is intuitively understood as a LOOP statement where you can control the index so it has the potential to be widely used.
But I have found one case where this model is broken. So when FOR UNTIL becomes pervasive in code, it will be used without discrimination and there will be cases where the intuition will be wrong, so it it can be Considered Harmful.
Evaluate the following pseudo code. What do you expect for N = 0 ?
DATA(iota) = VALUE table_of_integer( FOR idx = 0 UNTIL idx = N ( idx ) ).
- For N = 5, we create the table iota = #( ( 0 ) ( 1 ) ( 2 ) ( 3 ) ( 4 ) ).
- For N = 2, we create the table iota = #( ( 0 ) ( 1 ) ).
- For N = 1, we create the table iota = #( ( 0 ) ).
Scroll down for the solution.
The UNTIL behavior is documented:
- If the iteration variable var has a numeric data type, or the variable is of type d or t, THEN expr is optional. If THEN expr is not specified explicitly, THEN var + 1 is added implicitly or the value of the iteration variable is increased by 1 for every iteration.
So in FOR .. UNTIL loops, at least one iteration step is executed. In the example above, if N = 0 or -1, -2 actually for any value less than the start value, our test for equality yields an endless loop because the iteration is always executed at least once.
Note this does not happen with a FOR .. WHILE expression
- If the termination condition is specified after WHILE, the logical expression log_exp is evaluated after every iteration step. If the result of the logical expression is false, the iteration is ended. If the result of the logical expression is false even before the first iteration step, no iteration steps are executed.
This does not happen with LOOP AT itab [FROM idx1] [TO idx2]
- If the value of idx2 is less than the value of idx1, no processing takes place.
I currently consider this deviation in the UNTIL Conditional Iteration a breach of contract. Just like in the case of SELECT FOR ALL ENTRIES with a empty internal table, the mental model is broken.
ABAP has been successful at eliminating off by 1 errors in loops with the pervasive LOOP statement. FOR .. THEN … UNTIL expressions give us the power to control the loop index. But by enforcing at least one iteration they break the mental model and yield endless loops.
Although the behavior is documented, I currently consider this a bug. There are more expressive way to specify an endless loop.
- Can you think of a use case for this behavior?
- Can anybody please create an extended syntax check rule for this special case?
- An endless loop occurs.
The issue was observed on the SAP Cloud Platform, ABAP environment. After a trivial refactoring, the unit tests would abort with a runtime problem. Working with unit tests saved me, again.
Thanks for sharing this.
" I currently consider this a bug"
Yes, I have the same feeling. Even if it's documented, it's an unexpected behaviour, so for me it's also a bug. Especially if FOR ... WHILE doesn't have this.
In other languages, WHILE and UNTIL have the same essential difference.
So, it’s obviously not a bug, and personally I think it’s not error prone because it should be obvious to the vast majority of developers. Of course, it may happen that even developers who know that just do a mistake.
I would say that it happens very often, whoever the developer is, whatever the statement is, that there are wrong assumptions, which are later discovered. For example:
Some of them were so error prone that they were deprecated (header lines, ON CHANGE OF, etc.)
Again, I personally consider UNTIL obvious.
But your point is valid if UNTIL is misunderstood by many developers. Maybe via a poll/survey?
I can think of a FOR WHILE as an expression enable WHILE clause, but FOR UNTIL has no corresponding statement in ABAP. I think the difference with other languages is the implicit auto-increment.
Having a THEN clause makes it easier to spot the problem IMO,
so I will probably recommend to always write THEN in FOR .. UNTIL clauses.
Thank you for the feedback,
Poll? I've never used it. So now I know.
As has been said in other languages ( e.g. the BASIC I used in 1981 ) the problem is more obvious.
LET monsters = 0.
LET monsters = monsters + 1.
UNTIL monsters = 0.
Just by looking at it you can tell it will be an endless loop. A clear example of the "make bad code look bad" principle.
As you said if you write the ABAP equivalent as
FOR monsters = 0
THEN monsters = monsters + 1
UNTIL monsters = 0
Then it looks as obvioulsy wrong as the BASIC equivalent.
Some of the new ABAP constructs are anything but obvious and adoption will be slow to non-existnat if they (a) make the developer writing the ones head spin and (b) cannot be understood by anybody else coming along to change the code.
The villain in the picece is the implicit (invisible) commands e.g. an invisible THEN var + 1
Every time you have something going on in the background the developer has no idea is happening, it all ends in tears. A logical database, may in rest in peace, was a fine example.
SAP has a horrifying tendency to think if you make these hidden things optionalit will make the code shorter and easier tounderstand but I would disagree. I really do not like in the ABAP Restful Programming model all the implicit paramaters in method signatures which do not have to be declared, but then in the method you can write to them or read them and it looks like magic.
This may sound obvious but if you go right out of your way to hide something from a developer, how is that developer going to know it is there? It's like hiding a rattlesnake in their briefcase and then being all surprised when all hell breaks loose at work when the developer arrives and opens their briefcase.