FORTRAN is often viewed as an outdated and archaic language, but it is just as capable of doing any job that other compiled languages can. It’s thoroughly up to date, with the previous stable standard being released in 2018 (5779), and the previous in 2010 (5771). Modern FORTRAN – which is now just spelt Fortran, although I prefer the former – supports generics and modularisation, object-oriented programming, concurrent programming, and is even interoperable with C. This post will walk you through the basic structure of a FORTRAN programme. It’s designed for people who already know a little about programming, but haven’t worked with FORTRAN in the past.
There’s no reason not to use FORTRAN, provided you enjoy working with the language and you can use it within your environment. At the end of the day, the tools used are unimportant; only the ability of the final product to meet requirements matters. That being said, I wouldn’t recommend using FORTRAN if the rest of your company only understands Objective-C. That probably won’t go down well with your boss.
This post and any future posts regarding FORTRAN will not deal with language elements that are specific to fixed-form FORTRAN. If you’re working with legacy code or compilers, a lot of this may not apply to you. I will be following the convention of writing all keywords in capitals, though this is completely optional.
The initial programme template
All FORTRAN programs start with the following basic template:
PROGRAM programme_name
IMPLICIT none
! code goes here
END PROGRAM programme_name
Every FORTRAN programme starts with the keyword PROGRAM
followed by the name of the programme and ends with END PROGRAM
followed by the name of the programme. PROGRAM
is optional, but it is almost always included as it informs the compiler where the main routine of the programme begins.
Most modern FORTRAN programs follow this with IMPLICIT none
. This tells the compiler that no implicit types are used by this programme. This is because, if this is not set, FORTRAN will attempt to define any variables with the names i, j, k, l, m
and n
as integers, and all other variables as real. It is included in almost all code to prevent issues with mistaken type declarations. This statement comes straight after PROGRAM
. Only the USE
keyword may be placed before it, but we will come to that later.
The !
character indicates a comment. Everything after the !
on that line is ignored by the compiler, except when it is used in a character string.
The rest of the code for the programme follows these statements. FORTRAN programs do have a specific order, though. You can’t write anything in any order and expect it to work – just as main()
must be at the end of a C file.
Specification, Execution and Subprogram
The specification, or declaration, section is placed immediately after the implicit statement. This part of the programme specifies the data types of variables that will be used by the main programme. For instance, if our programme were to use an integer referenced as the_integer
and a character string referenced as the_character
, our programme would now look like this.
PROGRAM programme_name
IMPLICIT none
! Start of the specification part
! The integer variable
INTEGER :: the_integer
! The character variable
CHARACTER :: the_character
! End of the specification part
! The rest of the code goes here
END PROGRAM programme_name
As you can see in the code above, we now have two variables initialised. You should note that FORTRAN does not have a separate data type for strings. The character type covers both single characters and strings of characters. You do need to define how long the string will be, however, or the compiler will truncate it to a single character. Alternatively, you can include a wildcard, but this comes with its own drawbacks that I won’t be covering here.
INTEGER :: the_integer
CHARACTER (LEN=6) :: the_character
After the specification section of the programme, we have the execution section. This section handles the main programme logic that will be executed when the programme is run. Using what we have thus far, we can quickly demonstrate how this part of the programme might look.
PROGRAM programme_name
IMPLICIT none
! Start of the specification part
! The integer variable
INTEGER :: the_integer
! The character variable
CHARACTER (LEN=6) :: the_character
! End of the specification part
! Start of the execution part
! Putting the variables to use
the_character = "World!"
the_integer = 1
the_integer = 1 + the_integer
! Printing to the command line
PRINT *, "Hello ", the_character
PRINT *, the_integer
! End of the execution part
END PROGRAM programme_name
Now we have a functioning, albeit short, FORTRAN programme. But what about subprograms? To begin to understand subprograms, we have to separate subprograms into two different subtypes: functions and subroutines.
Functions
Functions are designed to emulate mathematical functions. They work just like functions in C languages, although functions are typically used if there is a single value that must be returned. Functions cannot return more than one value. The following is an example function that prompts for two integers, then adds two integers together.
INTEGER FUNCTION Addition(a, b)
IMPLICIT none
INTEGER :: a, b
Addition = a + b
END FUNCTION Addition
This function can then be called from the main programme code. It will execute, then return the value of Addition
. Note that the name of the function is used as the return statement, rather than using a keyword such as return
as is the case in C.
Subroutines
Unlike functions, subroutines are designed to be used when a return value is not required, or when more than one return value is required. Functions can serve the former purpose, but this is generally not how they are used. They are visually similar to functions, as you will see below.
SUBROUTINE Subtraction(a, b, c)
IMPLICIT none
INTEGER, INTENT(IN) :: a, b
INTEGER, INTENT(OUT) :: c
c = a - b
END SUBROUTINE Subtraction
There are a few key differences, however. Firstly, there is no type assigned to the subroutine. This is because subroutines do not have to return a value, nor do they have to return only a single value. Secondly, the variables that are going to be returned by the subroutine are included in the formal argument.
Finally, we have to declare what the purpose of our variables is, as you can see with INTENT(IN)
and INTENT(OUT)
. When the intent of a variable is IN
, that means the value of this variable is determined by the input of the subroutine’s formal argument. When the intent is OUT
, then the value will be determined within the subroutine, then returned to the part of the programme that called the subroutine. Both of these can be combined with the intent INOUT
.
Using functions and subroutines
Now that we know how functions and subroutines are written, we can see how they are implemented in programs.
PROGRAM programme_name
IMPLICIT none
! Start of the specification part
! The integer variable
INTEGER :: the_integer, the_output
! The character variable
CHARACTER (LEN=6) :: the_character
! End of the specification part
! Start of the execution part
! Putting the variables to use
the_character = "World!"
the_integer = 1
the_integer = 1 + the_integer
PRINT *, "Hello ", the_character
PRINT *, the_integer
! Calling the function Addition(a, b)
PRINT *, Addition(the_integer, the_integer)
! Calling the subroutine Subtraction(a, b, c)
CALL Subtraction(the_integer, the_integer, the_output)
PRINT *, the_output
! End of the execution part
! Start of the subprogram part
CONTAINS
INTEGER FUNCTION Addition(a, b)
IMPLICIT none
INTEGER :: a, b
Addition = a + b
END FUNCTION Addition
SUBROUTINE Subtraction(a, b, c)
IMPLICIT none
INTEGER, INTENT(IN) :: a, b
INTEGER, INTENT(OUT) :: c
c = a - b
END SUBROUTINE Subtraction
! End of the subprogram part
END PROGRAM programme_name
To separate out functions and subroutines from the main programme, we use the keyword CONTAINS
. This lets the compiler know that the code included after this is not a part of the main programme, and thus should be executed when called, rather than procedurally.Now that the subprograms are included in the entirety of our code thus far, we can see some differences between the two. Right off the bat, we can see that a new integer has been declared in the main programme. This integer, the_output
, is defined by the output of the subroutine. Unlike functions, subroutines must have their output assigned to variables declared by the main programme.
We can also see that functions and subroutines are called in a different manner. Functions can be called from within code as they can in C, but subroutines must be called with the CALL
keyword. This is because the compiler cannot predetermine how the output of a subroutine will be used if an output is provided. Functions will return a single output that it can interpret, however.
There’s just one more element of FORTRAN programs that we haven’t covered, and that is modules. As these separate out programme code into multiple files and introduce a whole new array of issues that encroach upon object-oriented development, I will cover these in a different post.
Concluding The Basic Structure of a FORTRAN Program
If you’ve gotten this far, congratulations. You’ve successfully learned more about FORTRAN than the majority of today’s programmers will ever know.
This post will, hopefully, give you everything you need to know to begin reading FORTRAN programs. You might not understand what the code does, but you’ll be able to pick out the structure of the programme and translate that into the concepts of a language that you are more familiar with.
As always, drop a comment below or join my Discord server if you have any comments or questions. If you enjoyed this article, check out some of my other development articles here.
why not just let the compiler default the variables that are always 100% of the time ints as ints?
I’m not 100% sure on their original reasoning, but FORTRAN’s implicit typing allows you to name variables without including their type and have the compiler understand what you’re trying to do. It saved writing out integer for every integer and real for every real, because you could just start your variable name with the appropriate letter. You can write less code, and people can instantly tell what the type is based on the first letter.
Love your blog, keep it up!