Learning the oldest programming language
While I probably should be learning a language like C, Go, or whatever new trendy language the ThePrimeagen mentions on Twitter (OCaml?), I'm going to attempt to learn Fortran[1].
A quick history
Fortran, which stands for FORmula TRANslator[2], was created at IBM by John Backus in 1957 for scientific applications and has apparently been popular for high-performance computing and benchmarking supercomputers in recent years. Fortran has had several subsequent releases since then; FORTRAN 77, Fortran 90, Fortran 95, Fortran 2003, Fortran 2008, and the latest Fortran 2018.
Which version of Fortran?
To understand what version of Fortran to learn/use, we first must understand the difference between fixed form and free form Fortran. The fixed form layout comes from the very beginning of Fortran, inherited from punch cards, and has odd restrictions about the column in which comments and statements are placed. The free form layout, first introduced in Fortran 90, removed special columns and added the ability to write comments wherever, and is what we'll be learning in this article. The compiler we'll be using is GNU Fortran, or gfortran
. You can install it via Homebrew (macOS) with the gcc
formula, or install it using a package manager for your OS. To tell gfortran
that your code uses the free form layout, set the file extension to .f90
or newer. The following comment on the Fortran discussion board explains this well.
The .f90 suffix means that the source code is free format, not that
the code conforms to the Fortran 90 standard. Code that uses the .f90
suffix can use features from any Fortran standard. All Fortran
compilers recognize .f90 as a suffix indicating free source form, but
some may not recognize a suffix such as .f95, .f03, .f08, or .f18.
Some users may have build tools that do not recognize suffixes other
than .f90. Most Fortran source code on GitHub that uses features from
a standard more recent than Fortran 90 still uses the .f90 suffix.
Understanding the syntax
Coming from TypeScript, and before that, Python, I'm very used to (and comfortable with) modern — you might say "aesthetic" — syntax . Although I wouldn't say Fortran syntax is quite modern, it seems to avoid the syntactic sugar nightmares that plague beginners in other languages[3]. Take a look at this helloworld.f90
example below.
program helloworld
print *, 'Hello, world!'
end program helloworld
Older Fortran programs required the use of SCREAMING_CASE for all keywords, but in modern Fortran you can and it is recommended to use snake_case (you can still use SCREAMING_CASE or any other case you want though).
Just from this small example we can gather that...
- Every Fortran program begins with
program <program-name>
and ends withend program <program-name>
. - To display text on the terminal we use
print *, '<message>'
.
The syntax for printing is a little funky though. What is that asterisk doing there? The asterisk, aside from being used as a mathematical operator, indicates the "default". So for print
, *
means "print to the default output channel" (or "print to the default output file unit" to be precise), which is typically going to be STDOUT.
I can't find exactly where this is documented but you don't actually need the start and end program <program-name>
; you could write a hello world program like this, though as I just mentioned this doesn't seem to be a common practice and isn't really very useful in any practical scenario.
print *, 'Hello, world!'; end
Here's another, slightly more complicated example.
program calculator
implicit none
real :: x, y, answer
character(1) :: choice
print *, 'x:'
read *, x
print *, 'y:'
read *, y
print *, '+, -, *, /:'
read *, choice
if (choice == '+') then
answer = x + y
end if
if (choice == '-') then
answer = x - y
end if
if (choice == '*') then
answer = x * y
end if
if (choice == '/') then
answer = x / y
end if
print *, 'Answer:', answer
end program calculator
Starting right at the top, we have something new: implicit none
. Added in Fortran 90, implicit none
disables implicit typing defaults and all variables must be explicitly declared. In Fortran, implicit typing is the practice of assigning default types to variables based on the character a variable name begins with. Variables starting with I
through N
are INTEGER
s, everything else is REAL
. It is "a legacy of the past" and usage of an implicit none
statement is "strongly advised" (implicit none - Fortran Wiki).
A common Fortran joke goes along the lines of “GOD is REAL, unless declared INTEGER"[4] because of implicit typing!
Moving on, we declare our first variables in this program.
real :: x, y, answer
character(1) :: choice
Here we are declaring x
, y
, and answer
with the REAL
type, and choice
with the CHARACTER
type. The REAL
type stores floating point numbers[5], and CHARACTER
... stores characters.
Next, we prompt the user for our x
and y
values.
print *, 'x:'
read *, x
print *, 'y:'
read *, y
Notice how we can take input from the user with read
and assign it to a value with the read *, <variable>
syntax. The asterisk here means read from the default input channel/file unit, which would be STDIN.
We do the same for prompting the user to select an operation.
print *, '+, -, *, /:'
read *, choice
Finally, we use a series of basic if-statements to calculate our answer and display it in the terminal.
if (choice == '+') then
answer = x + y
end if
if (choice == '-') then
answer = x - y
end if
if (choice == '*') then
answer = x * y
end if
if (choice == '/') then
answer = x / y
end if
print *, 'Answer:', answer
If we run this, we- wait. Did I even tell you how to compile a Fortran program yet?
How do I actually run this?
First, compile our calculator program with gfortran -o calculator calculator.f90
. Then you can run it with ./calculator
. If you only instruct gfortran
of the input file (gfortran calculator.f90
), the default output executable will be named a.out
.
Let's run our program now.
$ gfortran -o calculator calculator.f90
$ ./calculator
x:
10
y:
2
+, -, *, /:
*
Answer: 20.0000000
Pretty cool, huh?
A few improvements
Our calculator isn't perfect yet though. What if the user tries to divide by zero?
x:
10
y:
0
+, -, *, /:
/
Answer: Infinity
Probably not the answer you expected. Let's try to fix that.
if (choice == '/') then
if (y /= 0.0) then
answer = x / y
else
print *, 'Error: Division by zero is not allowed.'
stop
end if
end if
Here we use the inequality operator, /=
, to check if the y
value is zero. Now, if the user tries to divide by zero, we'll print an error message and use the stop
statement to end the program.
Great. We got rid of the zero division mess, but our code isn't pretty at all. Who wants a bunch of if statements? We can simplify this using the select case
statement (also known as the case
statement).
select case (choice)
case ('+')
answer = x + y
case ('-')
answer = x - y
case ('*')
answer = x * y
case ('/')
if (y /= 0.0) then
answer = x / y
else
print *, 'Error: Division by zero is not allowed.'
stop
end if
case default
print *, 'Invalid choice. Please choose +, -, *, or /.'
stop
end select
This also has the handy benefit of telling the user if they made an invalid choice while selecting the operation.
That’s just a quick introduction to a few modern Fortran features: declaring variables, printing and reading to and from the terminal, if
and select case
, and stop
. Next time, we’ll talk more about where Fortran is actually used, cooler things you can build with it, and how the Fortran language & community are rapidly modernizing!
Ironically, in the ~3-ish months since I started writing this article, ThePrimagen has recently said he "take[s] back everything i said about FORTRAN" — apparently having some interest in the language! ↩︎
According to sources listed on Fortran's Wikipedia, the name might also have stood for Formula Translating System or just Formula Translation. ↩︎
See The Rust programming language absolutely positively sucks : r/rust and Rust is a nightmare to learn coming from Java - community - The Rust Programming Language Forum. ↩︎
The first letter of "GOD", a "G", is not within I through N and is therefore of the
REAL
type ("GOD is REAL"). ↩︎You can also use
double precision
for larger (more precise) floating point numbers. ↩︎