The first programming language was Fortran (FORmula TRANslator). Since then, a lot of languages called "Fortran " were created, each bringing it closer to modern programming and further away from its sources.
It would be difficult to actually run extremely old Fortran code, as that worked with punch cards, and data tapes. Even by the time of Fortran 77 a lot of the ancient Fortran features were either gone or marked as obsolete. But let's do our best - using GNU Fortran in legacy mode, and trying to make the code as old style as I can get away with.
Whenever I mention Fortran in this post, I mean the really old stuff. So don't tell me about Fortran 2018 and how it can do object oriented concurrent crypto mining while running in WASM on a tablet or whatever kids are into these days.
Obviously we'll be writing the program in full caps, as lower case letters weren't invented until 1970s. Well, at least computers didn't usually have them, to save precious memory.
Unlike all modern, Fortran has fixed column layout. Here's one attempt at Hello, World:
C PRINTS "HELLO, WORLD!" PROGRAM HELLO 1 PRINT *, 'HELLO, WORLD!' NICE END
$ gfortran -std=legacy -o hello hello.f $ ./hello HELLO, WORLD!
What the hell is going on here?
- column 1 is comment indicator - if there's anything non-blank there, the whole line is a comment. So you can have
--comments in Fortran, whichever style you prefer! How convenient, right?
- columns 2-6 are for optional line number, we'll be doing goto a lot
- column 7 is line continuation indicator - if there's anything else than space (or
0for some reason) it continues previous line
- columns 8-72 are for the actual code
- columns 73+ are ignored, so they're essentially also comments
Weird? We're just getting started.
Fortran 77 introduced strings delimited by quotation marks. How did Fortran programmers do strings before then? You're in for a treat!
The old syntax for strings was character count, followed by
H, followed by the string. If you miscounted, and people did all the time, that was a syntax error.
I assure you, I'm not trolling, this was actually the thing.
C PRINTS "HELLO, WORLD!" PROGRAM HELLO 1 PRINT *, 13HHELLO, WORLD! EVEN NICER END
Fortran 77 introduced some sort of structured programming. In the olden days, everything was based on line numbers. Take a look at this loop:
PROGRAM LOOP PRINT *, 10HLOOP START DO 20 I = 10, 20, 2 10 PRINT *, 2HI= 20 PRINT *, I 30 PRINT *, 9HLOOP DONE END
./loop LOOP START I= 10 I= 12 I= 14 I= 16 I= 18 I= 20 LOOP DONE
DO 20 I = 10, 20, 2 means
for i = 10 to 20 by 2 loop. The loop goes until line number listed in the
DO statement, that is
20. After the loop is over it goes to next statement after statement numbered
As for declaring variables, any variable starting with I, J, K, L, M, or N as integer. Any other name is a float. Oh and variable names could have at most 6 characters originally.
Now that we know how to loop and print, it's fairly straightforward to do a FizzBuzz:
PROGRAM FIZZBUZZ DO 40 I = 1, 20 10 IF (MOD(I,15).NE.0) GOTO 20 PRINT *, 8HFIZZBUZZ CONTINUE 20 IF (MOD(I,5).NE.0) GOTO 30 PRINT *, 4HBUZZ CONTINUE 30 IF (MOD(I,3).NE.0) GOTO 40 PRINT *, 4HFIZZ CONTINUE 40 PRINT *, I END
That prints creatively formatted FizzBuzz:
$ ./fizzbuzz 1 2 FIZZ 3 4 BUZZ 5 FIZZ 6 7 8 FIZZ 9 BUZZ 10 11 FIZZ 12 13 14 FIZZBUZZ BUZZ FIZZ 15 16 17 FIZZ 18 19 BUZZ 20
i % 15.
CONTINUE goes to the next iteration of the loop.
This formatting is quite bad, so how could we improve it?
Let's write a very simple function, that doubles the integer it gets:
PROGRAM DOUBLING 10 FORMAT(7HDOUBLE(, I3, 2H)=, I4) DO 20 I = 1, 20 J = DOUBLE(I) 20 PRINT 10, I, J END FUNCTION DOUBLE(I) DOUBLE=I*2 END
And run it:
./double DOUBLE( 1)= 2 DOUBLE( 2)= 4 DOUBLE( 3)= 6 DOUBLE( 4)= 8 DOUBLE( 5)= 10 DOUBLE( 6)= 12 DOUBLE( 7)= 14 DOUBLE( 8)= 16 DOUBLE( 9)= 18 DOUBLE( 10)= 20 DOUBLE( 11)= 22 DOUBLE( 12)= 24 DOUBLE( 13)= 26 DOUBLE( 14)= 28 DOUBLE( 15)= 30 DOUBLE( 16)= 32 DOUBLE( 17)= 34 DOUBLE( 18)= 36 DOUBLE( 19)= 38 DOUBLE( 20)= 40
What's going on:
- we finally get nicely formatted output, that we can use
FORMATstatement to setup some format (in this case equivalent of
"double(%3d)=%4d\n") - each
FORMATstatement has a label (in this case
10), and it's used to refer to format number - these are not just for
PRINT 10, I, Juses the
10 FORMATwe setup before
FUNCTION DOUBLE(I) ... ENDdefines a function
- assigning to function name
DOUBLE = I*2sets which value will be returned, there's no direct equivalent of
Now that we know how to do a function, let's do a Fibonacci sequence. Oh wait, even Fortran 77 did not support recursive functions at all.
For now let's use some "modern" Fortran extensions to have recursion.
PROGRAM FIBONACCI 10 FORMAT(4HFIB(, I3, 2H)=, I10) DO 20 I = 1, 20 J = FIB(I) 20 PRINT 10, I, J END RECURSIVE FUNCTION FIB(I) RESULT(A) IF (I-2) 30,30,40 30 A=1 RETURN 40 A=FIB(I-1)+FIB(I-2) END
It prints what we'd expect:
FIB( 1)= 1 FIB( 2)= 1 FIB( 3)= 2 FIB( 4)= 3 FIB( 5)= 5 FIB( 6)= 8 FIB( 7)= 13 FIB( 8)= 21 FIB( 9)= 34 FIB( 10)= 55 FIB( 11)= 89 FIB( 12)= 144 FIB( 13)= 233 FIB( 14)= 377 FIB( 15)= 610 FIB( 16)= 987 FIB( 17)= 1597 FIB( 18)= 2584 FIB( 19)= 4181 FIB( 20)= 6765
There are a few things going on here:
RECURSIVE FUNCTION FIB(I) RESULT(A)- declares recursive function, we also need to declare return variable, as otherwise
FIB=FIB(I-1)+FIB(I-2)would be too confusing
RETURN- we can return early
IF (I-2) 30,30,40- the "arithmetic if", you pass a number, and three goto labels depending if the number is negative, zero, or positive - that's how Fortran programmers used to do
if (i <= 2)equivalent back in the days
But back in the olden days, recursion was not allowed. Fortunately we already know the loop-based algorithm:
PROGRAM FIBONACCI 10 FORMAT(4HFIB(, I3, 2H)=, I10) DO 20 I = 1, 20 J = FIB(I) 20 PRINT 10, I, J END FUNCTION FIB(I) K1 = 0 K2 = 1 DO 30 J = 1, I K3 = K1 + K2 K1 = K2 30 K2 = K3 FIB=K1 END
It outputs the same thing. Any variable starting with
K is also an integer, so we didn't need to declare anything.
As long as we're talking about these ancient versions, obviously not.
Some old languages like Forth or PostScript could enjoy a second life as an esoteric language, but I don't think old Fortran even has much potential there.
Supposedly modern Fortran generates slightly faster numerical code than C/C++ in some cases, so some numerical calculation libraries are still coded in Fortran (from what I remember, Fortran arrays can be assumed to not overlap, while C/C++ pointers can point anywhere, so Fortran compiler can do a few more optimizations), even if they are generally used from other languages. This is a somewhat marginal use, and you can probably get matching performance with some compiler hints, but in any case, this episode is only about old Fortran anyway.