Requesting insights on the BLOCK construct for scoped variable declarations

Hello, Fortran community!

I hope you’re having a good day. Following up on a recent comment by @Beliavsky (Fortran Discourse, GitHub), I’m reaching out on behalf of the team starting the open-catalog repository, for your thoughts and insights on the BLOCK construct.

The BLOCK construct, introduced in the Fortran 2008 standard, offers a way to declare variables within a limited scope. I’m interested in understanding the community’s views on recommending the usage of the construct for code organization. To this end, I have a couple of areas where your insights would be invaluable:

1. Adoption of Fortran 2008 features

The Fortran 2008 standard, which introduces the BLOCK construct, seems to have broad support across various compilers (e.g., gfortran, Intel, or Cray). However, the extent of its adoption within the community is still a bit unclear to me.

I’ve encountered some discussions and resources on modern Fortran usage, such as:

  • A Fortran Discourse thread on Fortran applications leveraging 2008 features.
  • A Fortran Wiki entry, containing around 50% of ~40 articles that are post-2008. This hints at a trend towards modern Fortran adoption, yet I’m seeking a wider perspective.

As a result, I’d love to hear about your experiences: are you primarily working with pre- or post-2008 Fortran? This information could significantly influence whether promoting the BLOCK construct is practical.

2. Programming style

Coming from a C/C++ background, I’m curious about the programming styles preferred within the Fortran community. Quoting this user, I think it’s undeniable that:

C and Fortran are different languages which require different coding practices.

Searching for discussions around the BLOCK construct, such as this Fortran Discourse entry, opinions on BLOCK seem to vary. Some prefer traditional top declarations for consistency, suggesting the division of procedures into smaller subroutines if necessary, instead of declaring multiple scopes. Others see value in declaring variables closer to their usage. There’s also discussion regarding specific scenarios, like declarations inside loop bodies or scoping for module imports.

I would really like to know your thoughts on whether you find BLOCK of interest for your programming! To facilitate a comprehensive understanding, I’m considering setting up a poll to gather opinions, such as:

  • I adhere to pre-Fortran 2008 standards, so I don’t have interest in this recommendation.
  • I prefer declarations at the top, regardless of BLOCK’s availability.
  • I’m open to promoting BLOCK for scoping, but I’m uncertain if I’d use it in my projects.
  • I support using BLOCK for more localized declaration/import of variables and modules.

Would this be OK?

Your expertise and feedback on this would be incredibly valuable! Thank you in advance for taking the time to help us better understand the preferences of the Fortran community :smile:

1 Like

I am still experimenting with both BLOCK and ASSOCIATE in my codes to understand when it is best to use and to not use those features.

1 Like

I can’t speak to the approval of the survey, but I can give my answer. Caveats: I am a retired software engineer with background in multiple languages and platforms. I am picking up modern Fortran as a hobbyist. My answer lies somewhere between the third and fourth options. I like the flexibility block structure can provide and believe it would help with the development and use of refactoring tools for Fortran.

I am not familiar enough with the standards to know how variable shadowing would be or should be handled.

1 Like

So far my primary use for BLOCK does not revolve around local variable declaration but for removing GO TO statements when refactoring old codes.

Example. If I have an old code with this common pattern

 subroutine sub
 ! Do a lot of stuff
100 continue

       If (some_error_condition) go to  500
.
.
.
       If (some_other condition) go to 200
       go to 100
    200 continue
! a lot of other code
           return
    500 print *,' error message'
           return
end subroutine sub

I’ll refactor this to

subroutine sub
! Do a lot of stuff
  error_block: BLOCK
     loop100: do
        if (some_error_condition) exit error_block
.
.
.
        if (some_other_condition) exit loop100
     end do loop100
! a lot of other code
     return
     end block error_block
     print *,'error message'
  end subroutine sub

I generally prefer defining all variables at the top of a routine but I do see some advantage to defining variables locally in a block (usually when I’m calculating local variables that I send to a call to a plot program)

here is a code fragment where I use pointer remapping to define an output variable for one of my vtk output routines. Having not to define the pointers at the top of the routine helps in this case


        CASE(FIELD_DENSITY)

          BLOCK
            Real(WP), Contiguous, Pointer :: rho(:)
            rho(1:nptot) => Qplt(:,:,1)
            Call VTKfile%writeScalarData("Density ",rho)
            If (ASSOCIATED(rho)) rho=>NULL()
          End BLOCK

        CASE(FIELD_MOMENTUM_VECTOR)

          BLOCK
            Real(WP), Contiguous, Pointer :: rhouv(:)
            rhouv(1:nptot) => Qplt(:,:,2)
            Call VTKfile%writeScalarData("RhoU ",rhouv)
            rhouv(1:nptot) => Qplt(:,:,3)
            Call VTKfile%writeScalarData("RhoV ",rhouv)
            If (ASSOCIATED(rhouv)) rhouv=>NULL()
          End BLOCK

3 Likes

On my side, I learned about associate and block say about 4/5 years ago and have slowly introduced them in production code, it did happen to me once to refactor first with associate and then prefered block. I’ve found both very useful and have helped in promoting a swift modernization to more readable and concise code… where a few years ago I would feel a heavy pressure towards "one day we should migrate everything to C++, now it is more like "Oh, this exists in Fortran? and since 2003/2008? :o " so the “pressure” has reduced :wink: but the modernization process takes time as refactoring is never P0 priority, but something done in the background.

So, I’m in this category

3 Likes

Thank you all for your feedback! I wasn’t expecting such quick responses :slight_smile:

That’s indeed a very powerful use of block :open_mouth:

Now that you mention it, my first contact with Fortran was a few years ago in a Master’s in High Performance Computing. The lecturers used to provide some Fortran example codes along with C/C++, but their expertise was clearly more in computational C. As a result, I also had the wrong impression that Fortran was a rather “old” language, lacking many features. However, not that I’m now slowly learning about modern Fortran, I honestly feel impressed at many times, and I have begun to understand why so many people like it :smile:

3 Likes

Dear @alvrogd

I would say I am programming in modern Fortran, at least with any features available in GFortran.

I am used for long to declare my variables at the top (and I think it is a good practice to first think about the variables needed by the algorithm). BLOCK could be useful for big procedures, but I try, if possible, to keep procedures not too long (ideally less than 2 screens is a good practice), so the top is never too far.

So far, my only use of BLOCK is when I need to add a variable in the code to try quickly something or find a bug. In that case, BLOCK...END BLOCK is also interesting as the temporary added code is clearly delimited (and all the more as I don’t use it for something else :smile:). If I delete it, no need to remind deleting temporary variables that I could have added at the top!

2 Likes

Actually the version after refactoring is a pretty legit way of error handling in Modern Fortran. Labeled blocks and loops allow for pretty powerful control flow, vastly superior to C/C++.

2 Likes

Yep. Provides a really clean way to handle multiple error conditions without a lot of returns or subroutine calls. What I do is use an enum to create named error codes and then set the error code specific to a type of error before exiting the block. Something like

enum, bind(c)
  enumerator :: error1=1, error2
end enum

error_block: block

    if (first_error) then
      error = error1
      exit error_block
    end if
    if (second_error) then
      error = error2
      exit error_block
   end if
   return ! if no error
end block error_block
select case(error)
   Case(error1)
       print *,' error1'
   Case(error2)
       print *,'error2'
 end select
end subroutine

If you want to handle a wide range of error condtiions across multiple routines you can replace the select case block with a global subroutine (or subroutines).

4 Likes

Funnily enough, in C/C++ I also use { ... } when I need to enclose temporal instructions for debugging, it’s super comfortable :smile:

2 Likes

Fortran is more verbose… :wink:

1 Like

I use block regularly but in specific cases all of which are used already discussed in this thread:
1] to allow EXIT from a section of code where the code does not fall within a DO or other named construct (eg , IF etc) that could otherwise have an EXIT. This also comes up on refactoring GOTOs.
2] Sections of experimental code within a routine, I can declare isolate the experimental code with a BLOCK add any new locals that are needed and indeed plonk an unconditional or conditional EXIT at the start of the block to bypass the experimental code. If this code is adopted it is often then easy to isolate it as a new subroutine as the unique locals needed are already clear.
3] I prefer to have all declarations at the top of subroutines but I don’t like really long subroutines as scrolling to look at decls is not nice so in limited cased local block decls are useful.

3 Likes