Description of old FORTRAN idioms

I have mentioned it before, but my description of old FORTRAN idioms, like COMMON blocks and the use of ARRAY(1) instead of ARRAY(*) and the like, is nearing completion. You can find the result at https://github.com/arjenmarkus/old-programming-idioms/tree/main/doc, in the hope it is useful. And otherwise it was fun anyway :slight_smile: to write it up.

I will not be making any more changes until I am back from my holiday (beginning of august), but I do appreciate comments, additions, corrections and other suggestions for improvement.

10 Likes

@Arjen
Excellent work. For those of us who have had the unfortunate task of trying to teach younger people about the differences in what I like to call Jurassic FORTRAN and Modern Fortran, having a Rosetta stone is a big help. One omission I would like to point out though is SENSE SWITCH :laughing:. Just kidding but you would be surprised how often it appears in old 1960 era NASA reports that contain full code listings.

Edit.

Also, if you can find a copy in a library somewhere, I would recommend you look at Fredrick Stuart’s " Fortran Programming" book circa 1970 (my first Fortran book). It has some nice fold out tables on the capabilities of various compilers and hardware from that period and also does a good job of covering what was I guess the unofficial standard at the time sometimes referred to as Fortran IV.

@Arjen,

Great effort to capture this.

Please take a look at this “Rosetta Stone” at Fortran-lang.org on Python-Fortran:
Python Fortran Rosetta Stone — Fortran Programming Language

Perhaps you will consider working Fortran-lang folks to create a “Rosetta Stone”, as mentioned by @rwmsu upthread, and strive toward a link to it under Learn — Fortran Programming Language?

It can then be a Community-driven effort where other contributors can submit PRs for additional idioms and other idiosyncrasies to keep in mind when it comes to working with or reviewing legacy FORTRAN code?

1 Like

Likely more suitable for short intermittent use, there is a digitized copy on archive.org of Wiley’s edition by 1969 (requires their free libary card; pending availability, the book can borrowed in slots of an hour each). Else, entry worldcat.org.

It so happens that the library of the university next door owns a paper copy of that book. So, I have asked to get it on loan :slight_smile:

Thanks for all your reactions - I will pick it up again in a few weeks’ time.

@Arjen thanks for sharing the link to your old-programming-idioms github repo, as it describes legacy Fortran constructs illustrated with example codes. We will add it to the online references of Fortran best practices that we are using to create the GitHub Open Catalog.

The first thing that comes to my mind is which are the Top 5 old-style Fortran idioms that the GitHub Open Catalog is not covering properly today. Any first suggestions or thoughts?

None yet, but I will have a look at the catalogue :slight_smile:

@Arjen - I’m a little late to the party, but here is some feedback. I hope these are useful!


2.2 – C (or c) in col 1 is a comment. A numeric character indicates a statement label (and continued into cols 2-5). * was a common extension. D also somewhat common for ‘debug’ lines – as you mention later.

If col 6 had anything other than blank or zero, it is a continuation. Some interpretations of F66 allowed for cols 1-5 to have other characters as commentary when col 6 indicated continuation. But this was later clarified to be illegal.

2.7 – Nowadays best to have subroutines and functions contained within modules to allow interfaces to be explicit. If it is required to have them in separate files, use a module with the necessary INCLUDE statements to incorporate the procedures into the module at compile time.

3.2.1 – I’ve never found a use for DIM either.

3.3 – Better yet, function A should be in a module – which is USEd by the program. If this is not possible, an interface block should be written so the compiler can do proper argument checking.

3.6 – Use procedures in a module. The module can hold state variables between calls to the procedures. (Better yet, define a derived type that can be passed around. That way your example could have multiple accumulators.)

3.8 – You can also make the function return value an ALLOCATABLE. Again, you’ll want the function to be in a module so the interface is explicit to the caller.

5.1 – Hollerith constants, by Standard, were only allowed in DATA statements and as actual arguments in procedure calls. Though as an extension, they were often allowed in other contexts (e.g., right hand side of assignment statements.)

As few as three , or as many as 20 characters might be stored in a scalar variable or individual array element. (E.g., machines with 24-bit word integers vs CDC 60-bit word double precision on either end of the spectrum.) Oftentimes programmers used 1 character for portability and ease of usage. Since could be wasteful of memory, a good compromise was 4 characters per word. This often required non-Standard shifting and masking operations to extract or insert individual characters into the appropriate positions.

DATA statements are only supposed to initialize COMMON block items from within BLOCK DATA program units. To do so otherwise is non-Standard.

5.2 – Again, use modules.

5.3 – Generally the hidden argument should be a size_t these days, not an int. The hidden arguments almost always follow the formal arguments – following the convention of the original unix f77 compiler.

7.1 – Again, use modules instead of COMMON blocks (and BLOCK DATA). EQUIVALENCE is not always so easy to untangle.

7.2 – There were implementations of Fortran 66 which used stack allocation of local variables. (e.g., Burroughs and at least one or two others.) Since the Standard was silent on the issue, these were perfectly legal implementations.

7.3 – DANGER – when using the more modern form of initializing during declaration, the variable automatically gets the SAVE attribute. This may not be what the programmer wants! Unless it is a PARAMETER (or initializer in a derived type definition), it may be better to initialize via an assignment statement.

(Yes, you mention this later in 10.4.)

7.4 – Again, use modules.

8.1 – Originally, units 1-4 were often connected to magnetic tape drives. Unit 5 for card input. Unit 6 for line printer output. And unit 7 for card punch output. But these varied considerably.

8.2 – Prior to Fortran 77, there was no standard direct access (random) file I/O. IBM had DEFINE FILE statement, and the apostrophie syntax in READ/WRITE statements. Other compilers varied considerably. Often resorting to library calls.

9.1 – CRAY pointers were generally used for dynamic memory allocation in the days before ALLOCATABLE. Very very rare to see them used as procedure pointers.

9.2 - .EQV./.NEQV. - Added in Fortran 77. So older codes didn’t use them.

9.3 – Prior to Fortran 77, there were no Standard OPEN or CLOSE (or INQUIRE) statements. Units were assumed to be pre-connected to files/devices at program startup time. Of course this was universally extended in pre-77 compilers in a variety of ways…

I often declare subroutines and functions after contains in a main program instead of a module, especially if they are unlikely to be wanted elsewhere and if the whole entire code is pretty short anyway. The interfaces are still explicit.

@wspector Thanks for this list :slight_smile: I will update the document with it.

@manuel.arenaz I had a quick look at the titles, but did not yet study the actual descriptions. That will come thoguh.

@Arjen - Happy to help. To further elaborate on a few things:

On 7.2 - stack allocation really started taking off in the mid-1980s, when computers with parallel processing started to proliferate. For example on the CRAY-XMP, with up to 4 cpus, the compilers implemented a stack option (static was still the default) to enable safe parallel execution of code. A set of library calls allowed the initiation and control of parallel tasks.

It is funny how stack allocation in Fortran was so controversial back then, whereas today it seems so obvious. It would have saved me so much time trying to cram large codes into small memories back then - along with the dynamic arrays that ALGOL 60 had (and eventually/finally incorporated into Fortran 90.) Using overlaying facilities was such a PITA…

On 8.1/8.2/9.3, remember that Fortran pre-dates fancy newfangled stuff like disk drives and operating systems. So in the earliest days, the I/O units could literally be directly controlling hardware peripherals. When the program was run, it was often the only code running on the machine. No OS. If fact the original Fortran I/O statements included things like READ INPUT TAPE and WRITE OUTPUT TAPE, and the ability to read ‘sense switches’ on the front panel. As disk drives and disk operating systems came into use, the I/O operations became more virtualized. The lack of OPEN/CLOSE statements, especially for disk files, and also random access on disk files, was pretty acute. Lots of non-Standard ways of providing the services. F77 finally provided a Standard conforming version of them.

On 9.1 - Now that I think about it, using CRAY pointers for procedure pointers was a DEC extension to the CRAY extension. As for CRAY code, one would use the heap manager subroutine calls, such as HPALLOC and HPDEALLOC, to obtain and free memory. These were equivalent to the malloc and free calls in C.