Has the Fortran committee considered allowing declaring variables anywhere instead of only at the beginning of procedures? This restriction makes it hard to write long procedures with many variables. Seems like another historical baggage just like implicit none.
You can already do so via the BLOCK construct. That also has the nice feature that you can exit the block, eliminating another need for GOTOs. See this silly example:
subroutine mysub
integer :: i
myblock: block
integer :: j
do i = 1,10
j = i * 2
if ( j > 5 ) exit myblock
enddo
write( *, * ) 'Final value: ', j
block myblock
! j is not defined here
(untested, so there can be syntax and other errors, but this is the idea)
What exactly is “hard” about it? Editors from the 70s allow you to have two windows on your source file, one centered on the declarations and one centered on executable code. Long procedures with many variables are discouraged in all languages, anyway. Code is read more often than it is written and it’s a distraction to search for variable declarations only to find yourself in the middle of some executable code.
I like to declare variables anywhere, and I don’t like to replace this function with block
. Now many other programming languages support this paradigm, and some skilled fortranners don’t like surprises, I don’t think it’s necessary to be so rigid.
The OP should also be aware of the associate construct.
PROGRAM ASSOCIATE_EXAMPLE
REAL :: MYREAL, X, Y, THETA, A
X = 0.42
Y = 0.35
MYREAL = 9.1
THETA = 1.5
A = 0.4
ASSOCIATE ( Z => EXP(-(X**2+Y**2)) * COS(THETA), V => MYREAL)
PRINT *, A+Z, A-Z, V
V = V * 4.6
END ASSOCIATE
PRINT *, MYREAL
END PROGRAM ASSOCIATE_EXAMPLE
You should stop because: there are two things important about declarations: one is the type and attributes, obviously. The other one is the scoping unit. Sprinkling declarations anywhere in the middle of executable code means that you don’t immediately see which scoping unit the declaration belongs to. You have to scroll upwards, reading every line, scanning for a BLOCK or a FUNCTION/SUBROUTINE. That is a lot of cognitive load imposed on the reader of the code and people will more often be readers than writers. Keeping declarations at the top of scoping units means that when you find the declaration, the statement starting a scoping unit is probably visible or a PageUp away.
I disagree with most of the current requests. None of these requests come from somebody that has written and maintained code that is sometimes older than 40 years. having experience of having to go back to update code I wrote in the early 80’s and seeing what the issues are I now do most of the following in code I write or update:
- Declare variables used throughout in a module that is used, staring with Implicit None.
- Name all End statements, and I prefer EndIf and EndDo as it makes it easier to read for me. Maintaining old code means more reading than writing as some else already said.
- I like having a continuation character at the end and start of a long line broken up.
- I like having the equations look like the mathematical ones because for me going back to verify the physics is often a priority.
I know it is mostly personal preferences but I find it short-sighted to use experience of a a decade or so without having to do long term support on software to ask for changes because typing a few extra characters is too much effort.
There is a proposal for this in the J3 GitHub repository:
Thank you for all your contributions, @Neels. Let’s please remain open to proposals and ideas from people of any experience and age. Instead, let’s criticize the proposals on technical merits.
Mainstream programming languages like C++ all allow users to declare a variable anywhere. This is not a magic feature. It has been around for too long.
Of course, you can always argue that a little bit of inconvenience does not matter and that Fortran is totally different from other programming languages. Then why not
disregard all the modern programming language features and simply stick to f77?
These features are pretty basic and have been used in other programming languages for a long time, and many of these features have already been requested by other Fortran users. I feel like maybe we should simply stick to f77 so that nobody will be unhappy.
I will note that this has been requested internally by some of my colleagues also.
What @themos wrote are excellent arguments, I tend to agree. But I will note that sometimes it does improve the readability to declare variables where they are used, as opposed to at the top. Yes, one should not write long procedures, except when it really is easier to just write a long procedure. Splitting it does not necessarily improve readability, because some numerical algorithms require to operate on a lot of arrays and variables, and it ends up being easier to just bite the bullet and put everything into one longer procedure. And currently it requires to have a long list of local variables at the top, and it gets messy. For example being able to declare variables inside a loop that are only used inside a loop would be helpful.
Modern Fortran has many features not in f77, such as free source form, end do, allocatable arrays, array operations, derived types, modules, argument intent, etc., that many of us value. Disagreeing with a particular addition is not the same as blanket opposition to progress.
What I feel is that many language designs now tend to please developers. This is considered user-friendly. After all, developers are the source of true creativity.
Obviously, some other language mature paradigms, after thinking about whether they are valuable, are patterns that can be used in Fortran for reference.
Looking at the Google C++ style guide on Local Variables we find:
Place a function’s variables in the narrowest scope possible, and initialize variables in the declaration.
C++ allows you to declare variables anywhere in a function. We encourage you to declare them in as local a scope as possible, and as close to the first use as possible. This makes it easier for the reader to find the declaration and see what type the variable is and what it was initialized to. In particular, initialization should be used instead of declaration and assignment, e.g.,:
I’ll note first that these guidelines do not apply directly to Fortran because of implicit save behavior and lack of class constructors (initialization).
What I would point out is that C++ allows you to declare variables anywhere, but C++ coders voluntarily settle on a style which they like and believe makes code more readable.
The Fortran philosophy is kind of reverse. The position of declarative statements is fixed by the standard. No need to write style guides. Whether this makes code more or less readable is a matter of debate and at some point boils down to personal preferences (biases) IMO.
According to the Zen of Python, explicit is better than implicit.
I believe F77 is more explicit than modern Fortran. For instance, fixed source form is more explicit than free source form, and you get finer control of which column to write your codes with fixed source form. Similarly, the go to statement is more explicit than exit and cycle, explicit shape arrays are certainly more explicit than allocatable arrays, simply using intrinsic types is more explicit than using derived types, modules also add complication since we can simply put all procedures and variables in a program, argument intent is also unnecessary because we can simply use comments to indicate the intent, etc. Further, the generics feature adds more implicitness to Fortran. We can simply write a procedure for each data type.
Doesn’t this do what you want?
Do i=1,N ; Block
Integer, Save :: a=0
! a retains value calculated across i-iterations and across calls to surrounding program unit.
a = some expression + a
End Block; End Do
Perhaps, and explicitness is but one of the factors. For example I also value conciseness, which sometimes goes against explicitness. Most (if not all) modern Fortran compilers will build F77 code. Specific F77 features that you like you can mix with newer features (except for source form, fixed and free form sources can’t be mixed in a single source file AFAIK).
I wonder if it’d be possible to create a “Zen of Fortran” that most Fortran programmers would mostly agree on?
Yes it does. It’s just unfortunate to have to write all the block
and end block
stuff like this.
Another limitation of block
for this use case is that if you want to declare a variable near where it’s first used, and you also want to use it further down in the program unit, the block
construct may become large. And if you do this for multiple variables that are introduced in several places in the program unit, you end up with multiple nested block
constructs, which would make the source cluttered and likely more difficult to read and understand. So, IMO using block
for this works against the benefits of the proposed feature.