A recursive function for reading lines (from a file)

Sometimes all we want to do is read a line of text from a file. But let’s make it more interesting and use recursion. The following Fortran module readlines reads lines from a text file. The read statement uses non-advancing I/O to read a small piece of the! current line in a file. If the end-of-line is reached, the file pointer is moved to the start of the next line. It uses a recursive subroutine piecemeal_readline() to do all the hard graft. In this case it reads chunks of text, 10 characters in length, and appends them to the line. When the end-of-line is encountered, the line is returned.

module readlines
   use iso_fortran_env
   implicit none


subroutine readline(lun, line, success)
   integer, intent(in) :: lun
   character(len=:), allocatable, intent(out) :: line
   logical, intent(out) :: success

   character(len=0) :: newline

   success = .true.
   call piecemeal_readline(newline)


   recursive subroutine piecemeal_readline(newline)
      character(len=*), intent(in) :: newline

      character(len=10) :: piece
      integer :: ierr, sz

      read(lun, '(a)', advance='no', size=sz, iostat=ierr) piece
      if (ierr /= 0 .and. ierr /= iostat_eor) then
         allocate(character(len=len(newline)):: line)
         line = newline
         success = .false.

      ! End of line?
      if (sz >= len(piece)) then
         call piecemeal_readline(newline // piece)
         allocate(character(len=len(newline)+sz):: line)
         line = newline // piece(1:sz)
         success = .true.
   end subroutine piecemeal_readline
end subroutine readline

end module readlines

Here is a passage to test what is happening (from Sense and Sensibility, Jane Austen):

I am excessively fond of a cottage; there is always so much comfort, so much
elegance about them. And I protest, if I had any money to spare, I should buy
a little land and build one myself, within a short distance of London, where I
might drive myself down at any time, and collect a few friends about me and be
happy. I advise everybody who is going to build, to build a cottage.

Basically let’s look at what happens at Line 34, which determines whether the end of a line has been encountered. If we process just the first line of the above text, using string chunks 10 in length, we get the following if the if statement is true:

 I am exces
 I am excessively fon
 I am excessively fond of a cot
 I am excessively fond of a cottage; ther
 I am excessively fond of a cottage; there is alway
 I am excessively fond of a cottage; there is always so much
 I am excessively fond of a cottage; there is always so much comfort, s
 I am excessively fond of a cottage; there is always so much comfort, so much

Basically, because it is not end-of-line, the current piece of text is appended to newline, which is then used as input to the recursive call of piecemeal_readline(). Each recursive call adds a chuck onto the line of text. When the end of line is encountered, the other part of the if statement is processed, returning the line as a string to the calling program. Here is a sample program that uses the module readlines.

program dynamo
   use readlines
   implicit none

   character(*), parameter :: filename = 'passage2.txt'

   integer :: lun
   logical :: ios
   character (len=:), allocatable :: str

   open(unit=lun, file=filename, status='old', action='read')

   ios = .true.
   do while (ios .eqv. .true.)
      call readline(lun,str,ios)
      if (ios .eqv. .true.) then
         print*, str
         print*, "Length = ", len(str)

end program dynamo

Here is the program running with the passage from above (in passage4.txt):

 I am excessively fond of a cottage; there is always so much comfort, so much
 Length =           77
 elegance about them. And I protest, if I had any money to spare, I should buy
 Length =           78
 a little land and build one myself, within a short distance of London, where I
 Length =           79
 might drive myself down at any time, and collect a few friends about me and be
 Length =           79
 happy. I advise everybody who is going to build, to build a cottage.
 Length =           68

More 2D strings in Fortran

Arrays of strings are easy to do. For example the following declaration produces an array of strings, where there are 5 strings, each of 10 characters in length:

character(len=12), dimension(4) :: a

This could also be written as:

character(len=12) :: a(4)

However the first declaration is the one that should be used, the second declaration is associated with old versions of Fortran, and should be avoided. Here is a program which deals with some strings, and prints out various things.

program strarr
   implicit none

   character(len=12), dimension(4) :: a
   a(1) = "Mandalorian"
   a(2) = "BobaFett"
   a(3) = "CaraDune"
   a(4) = "IG-11"

   print*, a
   print*, a(2)(5:8)
end program strarr

You will notice there are two types of indices: one for the index of the array, and another for the position of the character. These two indices are specified separately, with the index first, and the position of the character in the string second. So in the example a(2)(5:8) means array element 2, string indices 5 to 8. Note that because this is a string and not a character array, to access a single element you have to use a range in the for x:x. Here is the output from the program above.

 Mandalorian BobaFett    CaraDune    IG-11

The strings in the array could also be allocated, so that the storage for each string in the array is variable length. Here is a program that uses an array of allocatable strings.

program arrvarstr
   implicit none

   type :: varl
      character(len=:), allocatable :: name
   end type varl

   type(varl), dimension(4) :: a(4)
   integer :: i

   allocate(character(len=11) :: a(1)%name)
   a(1)%name = "Mandalorian"
   allocate(character(len=8) :: a(2)%name)
   a(2)%name = "BobaFett"
   allocate(character(len=8) :: a(3)%name)
   a(3)%name = "CaraDune"
   allocate(character(len=5) :: a(4)%name)
   a(4)%name = "IG-11"

   do i = 1,4
      print*, a(i)%name
   end do
   print*, a(2)%name(5:8)

end program arrvarstr

Here is the output:


Finally, there a variable length array of (static) strings. Here is an example of a variable length array of strings of size 20. The length of the array of strings is allocated to 40.

program arrvarlen

   implicit none

   character(len=20), dimension(:), allocatable :: c

   c(1) = "do"
   c(2) = "or"
   c(3) = "do not"

   c = [c, "there is no try"]

   print*, c

end program arrvarlen

Note there is also an interesting piece of code on Line 12. Here a new string is appended to the next position in the array (in this case element 4). Here is the output from the program:

 do                  or                  do not              there is no try

Calculating wind chill in Fortran

As temperatures fall and the wind howls, we begin hearing about the danger of “wind chill”. The “wind chill” factor W is reported by meteorologists during winter. W is an equivalent temperature that accounts for the increased chilling effects of wind on a human body. The wind chill factor combines the temperature and wind speed to tell you how cold the wind makes it “feel”. The coldest wind chill recorded in Canada was at Pelly Bay, Nunavut, on January 13, 1975, when 56 km/h winds (a wind chill factor of 3,357 watts/m²) made the temperature of -51°C feel more like -92°C.

The first wind chill formula and associated tables were based on research conducted by scientists Paul A. Siple and Charles F. Passel in Antarctica in the 1940s. The research found that the rate at which water freezes depends on three factors: how warm it was to begin with, the outside temperature and the wind speed. This Wind Chill Index was a 3-4 digit number with units of kilocalories per square metre per hour (kcal/m2/hr). For example 1400 was the threshold for frostbite. The original Siple-Passel equation for wind chill factor (°C) is:

C = (10.0√V - V + 10.5)(33-Ta)

where V is the wind velocity (m/s), and Ta was the air temperature (°C). One of the problems with this formula was that the units were not very user-friendly for the general public. In 2001 a new formula was derived , based on a model of how fast a human face loses heat and incorporates modern heat transfer theory, that is, the theory of how much heat is lost by the body to its surroundings during cold and windy days. (The face is the part of the body most often exposed to severe winter weather). It must be noted that although the wind chill factor is expressed on a temperature scale (the Celsius scale in Canada), it is not a temperature: it only expresses a human sensation. There are two formulas used by Environment Canada:

W = 13.12 + 0.6215Tair - 11.37V(10m)0.16 + 0.3965Tair V(10m)0.16
W = Tair + [(-1.59 + 0.1345Tair)/5]V(10m)

where W is the wind chill factor based on the Celsius temperature scale, Tair is the air temperature in degrees celsius, and V10m is the wind speed in km/h at 10 meters. The first equation is used when the temperature of the air is ≤ 0°C and the wind speed is ≥ 5km/h. Then second equation is used when the temperature of the air is ≤ 0°C and the wind speed is > 0km/h, but < 5km/h.

Here is the Fortran program to perform the calculation:

program wind_chill

    real :: airTemp, windS, windCF

    ! Obtain the user input
    write (*,*) 'Air temperature (Celsius): '
    read (*,*) airTemp
    write (*,*) 'Wind speed (km/hr): '
    read (*,*) windS

    if (airTemp <= 0.0) then
        if (windS >= 5.0) then
            windCF = 13.12 + 0.6215*airTemp - 11.37*windS**0.16 + &
        else if ((windS > 0.0) .and. (windS < 5.0)) then
            windCF = airTemp + ((-1.59+0.1345*airTemp)/5.0)*windS;
            write (*,*) 'There is no wind!'
        end if
        write (*,100) windCF
        100 format ('The temperature feels like ', F7.2, ' degrees Celsius')
        write (*,*) 'Unable to calculate - the air temperature is too high'
    end if

end program wind_chill

The program is inherently easy to write in Fortran. Here’s the program running:

Air temperature (Celsius):
Wind speed (km/hr):
The temperature feels like   -8.12 degrees Celsius

Translating an Algol-60 program to Fortran

This post deals with the simple task of translating an Algol-60 program to Fortran, and some simple tricks which can be used. The program in question calculates e to many digits.

There are many algorithms for calculating numbers like π and e. Normally e is calculated using the infinite series. These series often offer fast convergence, and are easy to calculate, however the problem is that computers are generally unable to accommodate more than 10-20 significant digits. For generating an accurate value for e, one has to turn to an alternative algorithm. One such algorithm is provided by Sale [1] in Algol-60 (or thereabouts). As there are no Algol-60 compilers out there, it is then a task of converting the program to another language. Here we have chosen Fortran, for reasons which will become clear as we progress through the discussion.

The first thing someone will say is that they don’t know Algol-60. While I’m sure that’s true of most people, if you have a basic clue about control structures in programs, and can read a program, deciphering what goes on here should not be that hard. The first thing to do is come reverse engineering. You have to understand what the algorithm embedded in the Algol-60 code does.

The first thing you might notice about this code is the fact that it allocates dynamic-type arrays within the code. This was not that unusual for Algol-type languages. Inputs to the procedure are n and d: where n is the number of decimal places to be calculated, and d is the array used to store them in. The use of the keyword loop does not in fact indicate a loop, but rather a label for that particular line. The if statement on the next line, if true, uses a goto to “loop” back to loop. In the Fortran this has been replaced by a real do while loop.

m := 4;
test := (n + 1) × 2.30258509;
loop: m := m + 1;
if m × (ln(m)-1.0)+0.5 × ln(6.2831852) × m)
   ≤ test then go to loop;
m = 4
test = (n+1) * 2.30258509
do while (m * (log(m*1.0)-1.0) + 0.5 *     
               log(6.2831852 * m) <= test)
   m = m + 1
end do

You will notice that variables are created when they are needed in the original program. As Fortran does not work in the same manner, these variables will be moved to the declaration block at the top of the subroutine. Here the value of m calculated in the previous code is used to allocate storage for the array coef. Fortran will allow allocate statements anywhere in a program. The loop to set the elements of coef to 1 is replaced with a simple statement in Fortran. The two do loops in Fortran fairly well mimic the for loops in Algol-60 program.

begin integer i,j,carry,temp
  integer array coed[2:m];
  for l:=2 step 1 until m do coef[j]:=1;
  d[0] := 2;
  sweep: for i:=1 step 1 until n do begin
    carry := 0;
    for j:=m step -1 until 2 do begin
      temp := coef[j] × 10 + carry;
      carry := temp ÷ j;
      coef[j] := temp - carry × j
    d[i] := carry
coef(2:m) = 1
d(0) = 2;
do i = 1, n
   carry = 0
   do j = m,2,-1
      temp = coef(j) * 10 + carry;
      carry = temp / j
      coef(j) = temp - carry * j
   end do
   d(i) = carry
end do

Now we can wrap these code segments inside a subroutine:

procedure ecalculation(n,d);
value n;
integer n;
integer array d;
begin integer m;
real test;

comment Add code segments 1 and 2

end of ecalculation;

subroutine ecalculation(n,d)
integer, intent(in) :: n
integer, allocatable, dimension(:), intent(out):: d
integer :: m, i, j, carry, temp
integer, allocatable, dimension(:) :: coef
real :: test


! Add code segments 1 and 2

end subroutine ecalculation

And finally add this subroutine into a Fortran main program wrapper:

program ecalc
   implicit none

   integer :: i,num
   integer, allocatable, dimension(:) :: eArr

   print*, "Number of digits?"
   read (*,*) num

   call ecalculation(num,eArr)
   print*, "e (actual) = 2.7182818284590452353602874"
   write(*,fmt="(a15)", advance="no") "e (calc)   = 2."
   do i = 1,num
      write(*,fmt="(i1)", advance="no") eArr(i)
   end do


   ! Add subroutine ecalculation

end program ecalc

Now we can compile the program and run a test:

 Number of digits?
e (actual) = 2.7182818284590452353602874
e (calc)   = 2.7182818284590452353602874


  1. Sale, A.H.J., “The calculation of e to many significant digits”, The Computer Journal, 11(2), pp.229-230 (1968)

The problem with π (iv) – Fortran to Ada

The Fortran program can also be translated to Ada. Below is the Ada program. It contains some code to output the resulting value of pi to the text file piCalc1000ADA.txt. In this code a is a “dynamic” array of integers, which is allocated using a declare block. In reality a is just of type pia, declared at a later point in the program.

with ada.Text_IO; use Ada.Text_IO;
with ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with ada.strings.unbounded; use ada.strings.unbounded;
with ada.strings.unbounded.Text_IO; use ada.strings.unbounded.Text_IO;

procedure piSpigotDYN is

    n, len : integer;
    q, x, nines, predigit : integer;
    type pia is array(integer range <>) of integer;
    infp : file_type;

    n := 1000;
    len := 10 * n / 3;

       a : pia(1..len);

    a := (1..len => 2);
    nines := 0;
    predigit := 0;

    for j in 1..n loop
       q := 0;
       for i in reverse 1..len loop
          x := 10 * a(i) + q * i;
          a(i) := x mod (2*i-1);
          q := x / (2*i-1);
       end loop;
       a(1) := q mod 10;
       q := q / 10;
       if q = 9 then
          nines := nines + 1;
       elsif q = 10 then
          for k in 1..nines loop
          end loop;
          predigit := 0;
          nines := 0;
          predigit := q;
          if nines /= 0 then
             for k in 1..nines loop
                nines := 0;
             end loop;
          end if;
       end if;
    end loop;

end piSpigotDYN;

Coding Fortran: A dynamic array of ragged strings

One of the tricks with Fortran is dealing with strings. Easy strings are easy, but more, shall we say challenging forms of strings can be tricky. Imagine if we wanted a dynamic array of ragged strings, i.e. strings that are all of different sizes.

One way of doing this is to create some derived data types. First we create one for a ragged string, ragged_str:

type :: ragged_str
   character(len=:), allocatable :: word
end type ragged_str

It has one member, word. This specifies that word is a string of deferred length, i.e. allocatable. Dynamic arrays are created using the attribute allocatable. Now we can create a second derived data type, strList, which creates a dynamic length array comprised of ragged strings. It’s member, list is a deferred length “array” of type ragged_str.

type :: strList
   type (ragged_str), dimension(:), allocatable :: list
end type strList

Now we can create a variable of this type:

type(strList) :: d

Now that all the data types have been created, they need to be allocated before being used. In the first case, we need to allocate the length of the list. Let’s say we need a list 10 words in length.


Note that the percentage symbol % is used to access the members of a derived type. Now we need to allocate the size for each of the 10 ragged string elements. For the first string, 7 characters in length, this can be done in the following manner:

allocate(character(len=7) :: d%list(1)%word)

As an example of how to use this to create a structure contains n words input by the user:

character (len=20) :: text
integer :: n, i, wl
print*, "How many words? "
read(*,*) n


do i = 1,n
   read(*,*) text
   wl = len(trim(text))
   allocate(character(len=wl) :: d%list(i)%word)
   d%list(i)%word = trim(text)
end do

Here is what happens in Lines 10-12:

  1. The length of the word, wl, is calculated (after using the function trim() to remove trailing blank characters from the string text).
  2. Allocate the size of the member word, based on wl.
  3. Assign the word text to the member d%list(i)%word.

NB: The allocatable arrays are always measured in number of array elements, not the number of bytes or other storage units. When they are allocated, the number of array elements in each dimension are specified, not the total amount of storage the array will consume.

Fortran – then versus now

If you don’t quite understand the difference between old and new Fortran, consider the following program. This (slightly modified) program comes from a 1968 book titled “Fortran Programming”, by Fredric Stuart. Likely written in Fortran IV, it basically just counts words and sentences. The program reads the standard input (via a redirected file), storing each of the 80 characters in an array, and then examines each one by one using a loop. The first comparison tests for the end-of-text terminator “/”. The next comparison checks for a period and increases the sentence count. The third comparison checks for black characters and increments the word count. The code isn’t exactly perfect as it will count every blank as a though a word appeared before it.

1     READ(*,10) LETT
      DO 6 K = 1,80
      IF (LETT(K)-LSLASH) 2,7,2
2     IF (LETT(K)-LPER) 4,3,4
3     NSENT = NSENT + 1
      GO TO 6
4     IF (LETT(K)-LBLANK) 6,5,6
5     NWORD = NWORD + 1
      write(*,*) k, " "
      GO TO 1
7     WRITE(*,11) NWORD, NSENT
10    FORMAT (80A1)
11    FORMAT (I16,' WORDS',10X,I16,' SENTENCES')

Now if we feed this program the following piece of text:

Cooking is a craft, I like to think, and a good cook is a craftsman -- not an
artist. There's nothing wrong with that: The great cathedrals of Europe were
built by craftsmen -- though not designed by them. Practicing your craft in
expert fashion is noble, honorable, and satisfying. /

We get:

              59 words                         3 sentences

Which isn’t exactly true. Maybe the original program ran on a specific type of text? Count the words and we get 48. So why is the program not working? Because it increments the word counter when a blank character is encountered. So three blank characters at the end of a sentence implies 3 words are counted. It will work properly with simple sentences only containing periods as punctuation. So when this program gets compiled in a modern Fortran compiler, it will choke of a series of deleted or less-than-palatable features – although it will compile the program. Let’s first morph this program to a more modern Fortran 2003 (F03) version:

!  Word and sentence count
   program wscount
   implicit none
   character, dimension(80) :: lett
   integer :: k, nword=0, nsent=0

10 format (80A1)
11 format (I16,' words',10X,I16,' sentences')

mloop: do
      read(*,10) lett
      do k = 1, 80
         if (lett(k) .eq. '/') then
            exit mloop
         elseif (lett(k) .eq. '.') then
            nsent = nsent + 1
         elseif (lett(k) .eq. ' ') then
            nword = nword + 1
         end if
      end do
   end do mloop
   write(*,11) nword, nsent

end program wscount

This updated version replaces the goto with a loop, rationalizes the do loop, and replaces the three arithmetic if statements with a nested if statement. The Hollerith constants (in the DATA statement) representing the slash, period and blank are replaced with characters within the logic statements, and all the variables are declared. The new program works exactly the same as the old one, i.e. it gives the same incorrect answer. So to fix that we have to add some functionality to deal with counting the words. Here is the modified program:

!  Word and sentence count

program wscount
      implicit none
      character, dimension(80) :: lett
      integer :: k, nword=0, nsent=0

10    format (80a)
11    format (I16,' words',10X,I16,' sentences')

mloop: do
         read(*,10) lett
         do k = 1, 80
            if (lett(k) .eq. '/') then
               exit mloop
            elseif (lett(k) .eq. '.') then
               nsent = nsent + 1
            elseif (lett(k) .eq. ' ' .and. (is_alpha(lett(k-1)) &
                    .or. is_punct(lett(k-1)))) then
               nword = nword + 1
            end if
         end do
      end do mloop
      write(*,11) nword, nsent


   pure logical function is_alpha(c)
      character(len=1), intent(in) :: c
      is_alpha = (c >= 'a' .and. c <= 'z') &
                 .or. (c >= 'A' .and. c <= 'Z')
   end function

   pure logical function is_punct(c)
      character(len=1), intent(in) :: c
      is_punct = (c == ',') .or. (c == '.') .or. (c == '?') &
                 .or. (c == '!') .or. (c == ":") .or. (c == ';')
   end function

end program wscount

The program now checks for a blank, and also if the character before the blank is a character (a-z, A-Z), or a punctuation mark (, . ? ! : ;). This requires the addition of two new functions is_alpha(), and is_punct(). Now when run it gives the correct count for words. Of course this snippet of a program will still not work on everything. What happens if a sentence has a real number in it, like 3.14159? It will count as an additional sentence.

But the point of this post is not really to illustrate how well the program works, but rather the difference between a Fortran program written in 1968 and one written in 2022. They are vastly different languages, with F03 having non of the previous spaghetti-like idiosyncrasies of its predecessors.

Why Fortran is a scientific powerhouse

People often are surprised to find out how prevalent Fortran is in the real world. For instance many climate models were originally written, and continue to be maintained in Fortran (languages like Julia are attempting to unseat it, but I think Julia would be a better language if it had an ISO standard associated with it).

One of the reasons Fortran still works is longevity. It’s much easier to build on something that was originally developed in Fortran (which is backwards compatible), rather than starting from scratch. People always make is seem so easy, but many a re-engineering project has come unstuck because of a failure to understand how the original software worked. Fortran is also exceptionally good at doing math, and climate models are nothing if not all about crunching data and doing math.

That’s not to say there aren’t challenges – but find me a language that doesn’t have some challenges. I don’t like it when people try to explain that “Python would be better”, because of some cockamamie idea that its syntax is better than Fortran. Sure it’s more succinct, but that’s purely for the benefit of the programmer, not the machine. And who is to say explicit typing doesn’t actually add some level of clarity, which is extremely important in programs that perform calculations.

So how many climate models are written in Fortran? Mendez et al. [1] analyzed 16 well known climate models in 2014, all were Fortran. So in reality, most if not all are written in Fortran. Why do they still use Fortran?

  • Performance – Fortran is fast, in cases faster than C, and climate modelling, like so much scientific computing is about speed. (Check out [2] for language benchmarks).
  • Parallelism – Performance necessitates parallelism, and Fortran was doing that before other languages even knew what it was.
  • Reuse – Backwards compatibility means that existing libraries can easily be reused.
  • Arrays – Fortran treats arrays with the respect they should have. It supports multi-dimensional arrays, slicing, reduction, reshaping, and many optimizations for array based calculations like vectorization.
  • Longevity – Climate models and the like can’t be implemented in languages that aren’t (i) backwards compatible, and (ii) always change. If you write code in Fortran today, it is still likely to work in 20 or 30 years time.
  • Effort – Do you know how much effort would be involved in re-engineering a model? New languages are problematic.
  • MPI – The message passing Interface (MPI) is the gateway to high-performance computing, and Fortran handles it very well.

Fortran was designed for scientific computing. Sure Fortran is old, but so is Unix, and C as well. Just because languages like Julia are newer, doesn’t mean it’s better. Everyone thought C++ would be some sort of coding saviour as well, and look how well that turned out. People sometimes think that if something is written in a language like R, then it is pure R… but fail to understand that R itself is not pure R. R is itself composed of code made up of R, C, and Fortran.

More info:

  1. Mendez, M., Tinetti, F.G., Overbey, J.L., “Climate models: Challenges for Fortran development tools”, in 2nd Int. Workshop on Software Engineering for High Performance Computing in Computational Science & Engineering (2014).
  2. The Computer Language Bennchmarks Game

P.S. Fortran often appears on those “outdated languages” lists, but the lists are often compiled by people who have little or no clue about anything to do with computing, let alone programming languages.

It’s not dead, Jim

I recently saw a list of programming languages that would be dying in 2023. On that list were the usual suspects, Fortran and Cobol. I kind of think this was one of those AI-derived articles, because it didn’t sound like a human had written it. Why surely these languages were on their way out right? Well, no more than they were in 1970. Why do people make these lists? Who really knows, but it is often through the use of unrealistic metrics such as the “number of jobs posted in language X”. Or writers that harp on old ideals such as “but Fortran produces spaghetti code”, never once actually having coded in a modern rendition of the language (or at all). Languages aren’t dead just because they don’t appear in some popularity chart.

In fact a recent global survey performed by Vanson Bourne, and commissioned by Microfocus, shows an amazing amount of Cobol code still in use, and a global footprint that continues to grow. The amount of Cobol code in daily use is 775-850 billion lines of code. To put this into context, estimates in the late 1990s put the number at 220 billion. Of the survey respondents, 72% see modernization of their Cobol code as an overall business strategy (i.e. not translating to another language). Dying? Unlikely anytime soon, because it forms the backbone for most of the world’s financial transactions.

What about Fortran? A dinosaur? Hardly. Sure it first appeared in 1957, which makes it 65, but C is only 50. And in reality, Fortran 90, a completely revamped language, is only 32 years old. As hardware has evolved, so too have programming languages. You can’t really put an age on programming languages – if a language can effectively turn an algorithm into a program, why beat yourself up over how “old” the language is. Fortran 2003 was a major revision of the language – It provided interoperability with C and included object-oriented programming support and subroutines could now be polymorphic based on their argument types like C++.

The actual problem is not one of the language itself, but one of the lack of individuals with the ability to program in these languages. Computer science departments have never really been bothered teaching Cobol, and Fortran has only been touted by the likes of math and physics departments. Everyone can code in C, or Python, but few can code in what are often termed “legacy” languages. It’s a function of CS departments only willing to teach certain languages, even when others are better for certain applications. And why not Fortran? It’s easier to code in than Python, and just as fast as C. I guess the fact that pointers are easier to use in Fortran makes it somewhat unpalatable in many CS departments.

If you wanted to future-proof your career, or work in cool places like NASA, then consider learning a language such as Cobol or Fortran. These and the likes of Lisp, C, C++, etc…. they won’t be going anywhere, anytime soon.