For/else (or do/else) in Fortran

There have been a few posts on loops as of late, which reminded me of something I’ve ran into before. In legacy code I often run into a block that looks something like this.

condition = .false.
do i = 1, 10
    ......
    if (condition) goto 20
enddo

error stop "Condition not met"

20 continue

I generally just leave these code blocks alone, as refactoring them to remove the goto feels error prone and not entirely sure it makes the code more readable.

Python has a really handy for/else construct, where if a loop completes it will execute a code block. Python For Else

I think some similar to this would be a great addition to Fortran. Feels it could be as simple as

condition = .false.
do i = 1, 10
    .......
    if (condition) exit
else
    ! This block only executes if the do loop doesn't exit.
    error stop "Condition not met"
enddo

I’m not aware of any obvious way to replicate this behavior without gotos in the current standard.

1 Like
conditional: block
   do i = 1, 10
      ! ...
      if (condition) exit conditional
   end do
   ! Loop did not exit prematurely as anticipated
   error stop "condition not met"
end block conditional
8 Likes
condition = .false.
do i = 1, 10
    ......
    if (condition) exit
enddo
if (.not.condition) error stop "Condition not met"
4 Likes

When I asked ChatGPT 4o, “in Fortran, how would you rewrite the [OP’s first code] without a goto and a numbered line?”, it gave effectively the same answer as @PierU

condition = .false.
do i = 1, 10
    ......
    if (condition) exit
enddo

if (.not. condition) then
    error stop "Condition not met"
end if

and exactly the same answer when asked to shorten the code, as does perplexity.ai and Claude 3.5 Sonnet.

1 Like

You can also test the loop control variable. For example:

condition = .false.
do, i=1, 10

if (condition) exit
end do

if (i > 10) then
error stop “condition not met”
end if

More generally though, a proposed structured control construct back in the 1970s was the so-called situation case. Charles Zahn wrote some papers on this, and Donald Knuth picked up on it. Zahn also used the MORTRAN Fortran preprocessor with macros to accept his SKOL language. SKOL supported the situation case. See: Zahn's construct - Wikipedia

2 Likes

The original code with the goto statement has the fewest lines.

In my opinion is also the clearest to understand – I use this all the time in our code. Why complicate things?

2 Likes

It’s not shorter than the code of @Pieru.

1 Like

This block seems like a do while construct would better portray the intent:

condition = .false.
i = 1
do while (.not. condition)
    if (i > 10) error stop "Condition not met"
    ......
    i = i + 1
end do

To me, the intent seems more like “do a thing until a condition is met but also terminate if it takes too long” rather than “do a thing for x iterations but the thing is only accepted if not all x iterations are required”. This intent can be further illustrated by including a max_iters variable:

condition = .false.
i = 1
max_iters = 10
do while (.not. condition)
    if (i > max_iters) error stop "Condition not met"
    ......
    i = i + 1
end do

This method becomes cumbersome if you have many conditions and/or want to keep track of which condition is met however. There is also the issue that it is very easy to forget to include either of the initializations of i and max_iters and to include the increment statement i = i + 1, where these errors would get caught by the compiler in a normal do loop. But I personally prefer this method for this type of situation.

2 Likes

I created a new issue for this at the proposals repository: "do / else" proposal · Issue #342 · j3-fortran/fortran_proposals · GitHub.

2 Likes

@everythingfunctional @certik Just out of curiosity: are there any Fortran constructs that imply fallthrough?

I mean, the if, select case|type|rank and where constructs execute at most one block of code, but the proposed for/else might execute two blocks.

And also (although somewhat moot), Python relies on indentation to avoid confusion, whereas Fortran does not:

do i = 1, 10
...
if (x == 0.1) stop
else
print *, 'item not found'
enddo
2 Likes

doelse or elsedo instead of else?

1 Like

Or loopended, or something else that carries the idea that the loop has ended “naturally”.

No, Fortran does not have constructs with fall through. As you said, all other conditional constructs execute at most one block of code. With that point, I’m really not sure I like this idea.

A minor tangent, but people have also been asking for exceptions for a long time, and if we followed the Python model there you’d end up with some really hard to follow control flow, i.e.

In Knuth’s classic paper “Structured Programming with go to Statements”, the single exit found/not_found search loop in this thread is his first example. He talks about several alternative ways of coding it. Midway into the paper he introduced Zahns situation case, he calls them ‘event indicators’, which can more generally handle loops with multiple exits. He also showed the construct in non-loop form - though without any examples.

(It is amazing to me that this paper was written 50 years ago! My has time flown by… Back then, case-like constructs were just starting to be recognized as necessary for structured programming. Only a few languages, like BCPL and BLISS, had the construct. Even elseif was controversial.)

Nowadays I think just using an extra variable to indicate which subset of a loop is being executed, a conditional EXIT after each subset, followed by a SELECT CASE after the loop, is fine. Same with a non-iterative BLOCK/END BLOCK - where one could do multiple EXITs from the block. E.g., something like:

integer :: step

step = 0 ! if n < 1
do, i=1, n
step = 1

if (…) exit
step = 2

if (…) exit
step = 3

end do
select case (step)
case (1)

case (2)

case (3)

case default

end select

Something like this can handle the Duplicate Code problem. It might also dovetail nicely with the F2023 enumeration types.

Many other languages have a try/throw/catch construct for exception handling. One could use it for a simple loop. (In fact a friend of mine, who years ago co-wrote much of Sun’s javac compiler, once told me that it was more common than he liked.) Except that exception handling usually implies a much more far-reaching capability than a simple loop or block.

2 Likes

I hope that Fortran never ever gets exceptions. ?: feels like enough C++ ugliness imported into Fortran.

That being said, some nicer error handling (such as standard error type at least) would prevent from every code handling it in a different way.

1 Like